2017-01-06 10:47:08 +00:00
/*global apf*/
define ( function ( require , exports , module ) {
main . consumes = [
"Plugin" , "settings" , "menus" , "preferences" , "commands" ,
"tabManager" , "ui" , "save" , "panels" , "tree" , "Menu" , "fs" ,
"dialog.question" , "clipboard"
] ;
main . provides = [ "tabbehavior" ] ;
return main ;
function main ( options , imports , register ) {
var Plugin = imports . Plugin ;
var settings = imports . settings ;
var tabs = imports . tabManager ;
var menus = imports . menus ;
var Menu = imports . Menu ;
var commands = imports . commands ;
var clipboard = imports . clipboard ;
var tree = imports . tree ;
var save = imports . save ;
var panels = imports . panels ;
var ui = imports . ui ;
var fs = imports . fs ;
var prefs = imports . preferences ;
var question = imports [ "dialog.question" ] . show ;
/***** Initialization *****/
var plugin = new Plugin ( "Ajax.org" , main . consumes ) ;
// var emit = plugin.getEmitter();
var mnuContext , mnuEditors , mnuTabs ;
var menuItems = [ ] , menuClosedItems = [ ] ;
var paneList = [ ] ;
var accessedPane = 0 ;
var cycleKeyPressed , changedTabs , unchangedTabs , dirtyNextTab , dirtyNextPane ;
var ACTIVEPAGE = function ( ) { return tabs . focussedTab ; } ;
2017-02-26 20:21:10 +00:00
var ACTIVEPATH = function ( ) { var tab = mnuContext . $tab || tabs . focussedTab ; return tab && ( tab . path || tab . relatedPath || tab . editor . getPathAsync ) ; } ;
2017-01-06 10:47:08 +00:00
var MORETABS = function ( ) { return tabs . getTabs ( ) . length > 1 ; } ;
var MORETABSINPANE = function ( ) { return tabs . focussedTab && tabs . focussedTab . pane . getTabs ( ) . length > 1 ; } ;
var MOREPANES = function ( ) { return tabs . getPanes ( ) . length > 1 ; } ;
var movekey = "Command-Option-Shift" ;
var definition = [
[ "clonetab" , "" , "" , ACTIVEPAGE , "create a new tab with a view on the same file" ] ,
[ "closetab" , "Option-W" , "Alt-W" , ACTIVEPAGE , "close the tab that is currently active" ] ,
[ "closealltabs" , "Option-Shift-W" , "Alt-Shift-W" , ACTIVEPAGE , "close all opened tabs" ] ,
[ "closeallbutme" , "Option-Ctrl-W" , "Ctrl-Alt-W" , MORETABS , "close all opened tabs, except the tab that is currently active" ] ,
[ "gototabright" , "Command-]" , "Ctrl-]" , MORETABSINPANE , "navigate to the next tab, right to the tab that is currently active" ] ,
[ "gototableft" , "Command-[" , "Ctrl-[" , MORETABSINPANE , "navigate to the next tab, left to the tab that is currently active" ] ,
[ "movetabright" , movekey + "-Right" , "Ctrl-Meta-Right" , MORETABS , "move the tab that is currently active to the right. Will create a split tab to the right if it's the right most tab." ] ,
[ "movetableft" , movekey + "-Left" , "Ctrl-Meta-Left" , MORETABS , "move the tab that is currently active to the left. Will create a split tab to the left if it's the left most tab." ] ,
[ "movetabup" , movekey + "-Up" , "Ctrl-Meta-Up" , MORETABS , "move the tab that is currently active to the up. Will create a split tab to the top if it's the top most tab." ] ,
[ "movetabdown" , movekey + "-Down" , "Ctrl-Meta-Down" , MORETABS , "move the tab that is currently active to the down. Will create a split tab to the bottom if it's the bottom most tab." ] ,
[ "tab1" , "Command-1" , "Ctrl-1" , null , "navigate to the first tab" ] ,
[ "tab2" , "Command-2" , "Ctrl-2" , null , "navigate to the second tab" ] ,
[ "tab3" , "Command-3" , "Ctrl-3" , null , "navigate to the third tab" ] ,
[ "tab4" , "Command-4" , "Ctrl-4" , null , "navigate to the fourth tab" ] ,
[ "tab5" , "Command-5" , "Ctrl-5" , null , "navigate to the fifth tab" ] ,
[ "tab6" , "Command-6" , "Ctrl-6" , null , "navigate to the sixth tab" ] ,
[ "tab7" , "Command-7" , "Ctrl-7" , null , "navigate to the seventh tab" ] ,
[ "tab8" , "Command-8" , "Ctrl-8" , null , "navigate to the eighth tab" ] ,
[ "tab9" , "Command-9" , "Ctrl-9" , null , "navigate to the ninth tab" ] ,
[ "tab0" , "Command-0" , "Ctrl-0" , null , "navigate to the tenth tab" ] ,
[ "revealtab" , "Command-Shift-L" , "Ctrl-Shift-L" , ACTIVEPATH , "reveal current tab in the file tree" ] ,
2017-02-19 12:00:58 +00:00
[ "nexttab" , "Option-Tab" , "Ctrl-Tab|Alt-`" , MORETABSINPANE , "navigate to the next tab in the stack of accessed tabs" ] ,
[ "previoustab" , "Option-Shift-Tab" , "Ctrl-Shift-Tab|Alt-Shift-`" , MORETABSINPANE , "navigate to the previous tab in the stack of accessed tabs" ] ,
2017-01-06 10:47:08 +00:00
[ "nextpane" , "Option-ESC" , "Ctrl-`" , MOREPANES , "navigate to the next tab in the stack of panes" ] ,
[ "previouspane" , "Option-Shift-ESC" , "Ctrl-Shift-`" , MOREPANES , "navigate to the previous tab in the stack of panes" ] ,
[ "gotopaneright" , "Ctrl-Meta-Right" , "Ctrl-Meta-Right" , null , "navigate to the pane on the right" ] ,
[ "gotopaneleft" , "Ctrl-Meta-Left" , "Ctrl-Meta-Left" , null , "navigate to the pane on the left" ] ,
[ "gotopaneup" , "Ctrl-Meta-Up" , "Ctrl-Meta-Up" , null , "navigate to the pane on the top" ] ,
[ "gotopanedown" , "Ctrl-Meta-Down" , "Ctrl-Meta-Down" , null , "navigate to the pane on the bottom" ] ,
[ "reopenLastTab" , "Option-Shift-T" , "Alt-Shift-T" , function ( ) {
return menuClosedItems . length ;
} , "reopen last closed tab" ] ,
[ "closealltotheright" , "" , "" , function ( ) {
var tab = mnuContext . $tab || mnuContext . $pane && mnuContext . $pane . getTab ( ) ;
if ( tab ) {
var pages = tab . pane . getTabs ( ) ;
return pages . pop ( ) != tab ;
}
} , "close all tabs to the right of the focussed tab" ] ,
[ "closealltotheleft" , "" , "" , function ( ) {
var tab = mnuContext . $tab || mnuContext . $pane && mnuContext . $pane . getTab ( ) ;
if ( tab ) {
var pages = tab . pane . getTabs ( ) ;
return pages . length > 1 && pages [ 0 ] != tab ;
}
} , "close all tabs to the left of the focussed tab" ] ,
[ "closepane" , "Command-Ctrl-W" , "Ctrl-W" , function ( ) {
return mnuContext . $tab || mnuContext . $pane || tabs . getTabs ( ) . length ;
} , "close this pane" ] ,
[ "nosplit" , "" , "" , null , "no split" ] ,
[ "hsplit" , "" , "" , null , "split the current pane in two columns and move the active tab to it" ] ,
[ "vsplit" , "" , "" , null , "split the current pane in two rows and move the active tab to it" ] ,
[ "twovsplit" , "" , "" , null , "create a two pane row layout" ] ,
[ "twohsplit" , "" , "" , null , "create a two pane column layout" ] ,
[ "foursplit" , "" , "" , null , "create a four pane layout" ] ,
[ "threeleft" , "" , "" , null , "create a three pane layout with the stack on the left side" ] ,
[ "threeright" , "" , "" , null , "create a three pane layout with the stack on the right side" ]
] ;
var loaded = false ;
function load ( ) {
if ( loaded ) return false ;
loaded = true ;
// Settings
settings . on ( "read" , function ( e ) {
settings . setDefaults ( "user/general" , [ [ "revealfile" , false ] ] ) ;
var list = settings . getJson ( "state/panecycle" ) ;
if ( list ) {
list . remove ( null ) ;
paneList = list ;
}
2017-02-19 12:00:58 +00:00
} , plugin ) ;
2017-01-06 10:47:08 +00:00
settings . on ( "write" , function ( e ) {
var list ;
if ( paneList . changed ) {
list = [ ] ;
paneList . forEach ( function ( tab , i ) {
if ( tab && tab . name )
list . push ( tab . name ) ;
} ) ;
settings . setJson ( "state/panecycle" , list ) ;
paneList . changed = false ;
}
2017-02-19 12:00:58 +00:00
} , plugin ) ;
2017-01-06 10:47:08 +00:00
// Preferences
prefs . add ( {
"General" : {
"Tree & Navigate" : {
"Reveal Active File in Project Tree" : {
type : "checkbox" ,
position : 4000 ,
path : "user/general/@revealfile"
}
}
}
} , plugin ) ;
// Commands
definition . forEach ( function ( item ) {
commands . addCommand ( {
name : item [ 0 ] ,
bindKey : { mac : item [ 1 ] , win : item [ 2 ] } ,
group : "Tabs" ,
hint : item [ 4 ] ,
isAvailable : item [ 3 ] ,
exec : function ( editor , arg ) {
if ( arg && ! arg [ 0 ] && arg . source == "click" )
arg = [ mnuContext . $tab , mnuContext . $pane ] ;
plugin [ item [ 0 ] ] . apply ( plugin , arg ) ;
}
} , plugin ) ;
} ) ;
commands . addCommand ( {
name : "refocusTab" ,
bindKey : { mac : "Esc" , win : "Esc" , position : - 1000 } ,
group : "Tabs" ,
isAvailable : function ( ) {
var el = apf . activeElement ;
if ( el && ( el . tagName == "page" || el . tagName == "menu" ) )
return false ;
return ! ! tabs . focussedTab ;
} ,
exec : function ( e ) {
if ( tabs . focussedTab )
tabs . focusTab ( tabs . focussedTab ) ;
} ,
passEvent : true
} , plugin ) ;
commands . addCommand ( {
name : "copyFilePath" ,
group : "" ,
isAvailable : function ( ) {
var el = apf . popup . getCurrentElement ( ) ;
if ( el && el . visible ) {
if ( el . $tab )
return ! ! ( el . $tab . path || el . $tab . relatedPath || el . $tab . editor . getPathAsync ) ;
}
return true ;
} ,
exec : function ( editor , args ) {
var text = "" ;
var el = apf . popup . getCurrentElement ( ) ;
var fromContextMenu = args && args . source == "click" ;
var tab ;
if ( ! fromContextMenu || ! el ) {
tab = tabs . focussedTab ;
text = tab . path || tab . relatedPath ;
}
else if ( el . name == "mnuCtxTree" ) {
text = tree . selectedNodes . map ( function ( n ) {
return n . path ;
} ) . join ( "\n" ) ;
}
else if ( el . $tab ) {
tab = el . $tab ;
text = tab . path || tab . relatedPath ;
}
if ( text ) {
clipboard . clipboardData . setData ( "text/plain" , text ) ;
}
else if ( tab && tab . editor . getPathAsync ) {
tab . editor . getPathAsync ( function ( err , text ) {
if ( ! err && text )
clipboard . clipboardData . setData ( "text/plain" , text ) ;
} ) ;
}
}
} , plugin ) ;
// General Menus
menus . addItemByPath ( "File/~" , new ui . divider ( ) , 100000 , plugin ) ;
menus . addItemByPath ( "File/Close File" , new ui . item ( {
command : "closetab"
} ) , 110000 , plugin ) ;
menus . addItemByPath ( "File/Close All Files" , new ui . item ( {
command : "closealltabs"
} ) , 120000 , plugin ) ;
mnuTabs = new ui . menu ( ) ;
menus . addItemByPath ( "Window/Tabs" , mnuTabs , 10100 , plugin ) ;
menus . addItemByPath ( "Window/Tabs/Close Pane" , new ui . item ( {
command : "closepane"
} ) , 100 , plugin ) ;
menus . addItemByPath ( "Window/Tabs/Close All Tabs In All Panes" , new ui . item ( {
command : "closealltabs"
} ) , 200 , plugin ) ;
menus . addItemByPath ( "Window/Tabs/Close All But Current Tab" , new ui . item ( {
command : "closeallbutme"
} ) , 300 , plugin ) ;
menus . addItemByPath ( "Window/Tabs/~" , new ui . divider ( ) , 1000000 , plugin ) ;
menus . addItemByPath ( "Window/Tabs/Split Pane in Two Rows" , new ui . item ( {
command : "vsplit"
} ) , 1000100 , plugin ) ;
menus . addItemByPath ( "Window/Tabs/Split Pane in Two Columns" , new ui . item ( {
command : "hsplit"
} ) , 1000200 , plugin ) ;
menus . addItemByPath ( "Window/Tabs/~" , new ui . divider ( ) , 1000300 , plugin ) ;
menus . addItemByPath ( "Window/Tabs/~" , new apf . label ( {
class : "splits" ,
2017-08-05 20:14:37 +00:00
caption : [
[ "span" , { class : "nosplit" } ] ,
[ "span" , { class : "twovsplit" } ] ,
[ "span" , { class : "twohsplit" } ] ,
[ "span" , { class : "foursplit" } ] ,
[ "span" , { class : "threeleft" } ] ,
[ "span" , { class : "threeright" } ] ,
] ,
2017-01-06 10:47:08 +00:00
onclick : function ( e ) {
var span = e . htmlEvent . target ;
if ( ! span || span . tagName != "SPAN" ) return ;
plugin [ span . className ] ( ) ;
mnuTabs . hide ( ) ;
}
} ) , 1000400 , plugin ) ;
menus . addItemByPath ( "Window/~" , new ui . divider ( ) , 9000 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/" , null , 9100 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Tab to the Right" , new ui . item ( {
command : "gototabright"
} ) , 100 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Tab to the Left" , new ui . item ( {
command : "gototableft"
} ) , 200 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Next Tab in History" , new ui . item ( {
command : "nexttab"
} ) , 300 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Previous Tab in History" , new ui . item ( {
command : "previoustab"
} ) , 400 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/~" , new ui . divider ( ) , 500 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Move Tab to Right" , new ui . item ( {
command : "movetabright"
} ) , 600 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Move Tab to Left" , new ui . item ( {
command : "movetableft"
} ) , 700 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Move Tab to Up" , new ui . item ( {
command : "movetabup"
} ) , 800 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Move Tab to Down" , new ui . item ( {
command : "movetabdown"
} ) , 900 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/~" , new ui . divider ( ) , 1000 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Go to Pane to Right" , new ui . item ( {
command : "gotopaneright"
} ) , 1100 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Go to Pane to Left" , new ui . item ( {
command : "gotopaneleft"
} ) , 1200 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Go to Pane to Up" , new ui . item ( {
command : "gotopaneup"
} ) , 1300 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Go to Pane to Down" , new ui . item ( {
command : "gotopanedown"
} ) , 1400 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/~" , new ui . divider ( ) , 1500 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Next Pane in History" , new ui . item ( {
command : "nextpane"
} ) , 1600 , plugin ) ;
menus . addItemByPath ( "Window/Navigation/Previous Pane in History" , new ui . item ( {
command : "previouspane"
} ) , 1700 , plugin ) ;
// Tab Helper Menu
menus . addItemByPath ( "Window/~" , new ui . divider ( ) , 10000 , plugin ) ;
mnuTabs . addEventListener ( "prop.visible" , function ( e ) {
if ( e . value ) {
if ( mnuTabs . opener && mnuTabs . opener . parentNode . localName == "tab" ) {
mnuContext . $pane = mnuTabs . opener . parentNode . cloud9pane ;
mnuContext . $tab = mnuContext . $pane . getTab ( ) ;
}
updateTabMenu ( ) ;
}
else {
removeContextInfo ( e ) ;
}
if ( mnuTabs . opener && mnuTabs . opener [ "class" ] == "tabmenubtn" )
apf . setStyleClass ( mnuTabs . $ext , "tabsContextMenu" ) ;
else
apf . setStyleClass ( mnuTabs . $ext , "" , [ "tabsContextMenu" ] ) ;
} , true ) ;
// Tree Context Menu
menus . addItemByPath ( "context/tree/Copy file path" , new ui . item ( {
command : "copyFilePath"
} ) , 800 , plugin ) ;
menus . addItemByPath ( "context/tree/~" , new ui . divider ( { } ) , 850 , menus ) ;
// Tab Context Menu
mnuContext = new Menu ( { id : "mnuContext" } , plugin ) . aml ;
menus . addItemByPath ( "context/tabs/" , mnuContext , 0 , plugin ) ;
function removeContextInfo ( e ) {
if ( ! e . value ) {
// use setTimeout because apf closes menu before menuitem onclick event
setTimeout ( function ( ) {
mnuContext . $tab = null ;
mnuContext . $pane = null ;
} ) ;
}
}
mnuContext . on ( "prop.visible" , removeContextInfo , false ) ;
menus . addItemByPath ( "Reveal in File Tree" , new ui . item ( {
command : "revealtab"
} ) , 100 , mnuContext , plugin ) ;
menus . addItemByPath ( "~" , new ui . divider ( ) , 200 , mnuContext , plugin ) ;
menus . addItemByPath ( "Copy file path" , new ui . item ( {
command : "copyFilePath"
} ) , 230 , mnuContext , plugin ) ;
menus . addItemByPath ( "~" , new ui . divider ( ) , 260 , mnuContext , plugin ) ;
menus . addItemByPath ( "Close Tab" , new ui . item ( {
command : "closetab"
} ) , 300 , mnuContext , plugin ) ;
menus . addItemByPath ( "Close All Tabs" , new ui . item ( {
command : "closepane"
} ) , 450 , mnuContext , plugin ) ;
menus . addItemByPath ( "Close Other Tabs" , new ui . item ( {
command : "closeallbutme"
} ) , 500 , mnuContext , plugin ) ;
menus . addItemByPath ( "Close Tabs to the Left" , new ui . item ( {
command : "closealltotheleft"
} ) , 600 , mnuContext , plugin ) ;
menus . addItemByPath ( "Close Tabs to the Right" , new ui . item ( {
command : "closealltotheright"
} ) , 700 , mnuContext , plugin ) ;
menus . addItemByPath ( "~" , new ui . divider ( ) , 750 , mnuContext , plugin ) ;
menus . addItemByPath ( "Split Pane in Two Rows" , new ui . item ( {
command : "vsplit"
} ) , 800 , mnuContext , plugin ) ;
menus . addItemByPath ( "Split Pane in Two Columns" , new ui . item ( {
command : "hsplit"
} ) , 900 , mnuContext , plugin ) ;
menus . addItemByPath ( "~" , new ui . divider ( ) , 1000 , mnuContext , plugin ) ;
menus . addItemByPath ( "Duplicate View" , new ui . item ( {
command : "clonetab"
} ) , 1010 , mnuContext , plugin ) ;
menus . addItemByPath ( "View/~" , new ui . divider ( ) , 800 , plugin ) ;
menus . addItemByPath ( "View/Layout/" , null , 900 , plugin ) ;
menus . addItemByPath ( "View/Layout/Single" , new ui . item ( {
command : "nosplit"
} ) , 100 , mnuContext , plugin ) ;
menus . addItemByPath ( "View/Layout/Vertical Split" , new ui . item ( {
command : "twovsplit"
} ) , 100 , mnuContext , plugin ) ;
menus . addItemByPath ( "View/Layout/Horizontal Split" , new ui . item ( {
command : "twohsplit"
} ) , 200 , mnuContext , plugin ) ;
menus . addItemByPath ( "View/Layout/Cross Split" , new ui . item ( {
command : "foursplit"
} ) , 300 , mnuContext , plugin ) ;
menus . addItemByPath ( "View/Layout/Split 1:2" , new ui . item ( {
command : "threeright"
2017-02-19 15:01:52 +00:00
} ) , 400 , mnuContext , plugin ) ;
2017-01-06 10:47:08 +00:00
menus . addItemByPath ( "View/Layout/Split 2:1" , new ui . item ( {
command : "threeleft"
} ) , 500 , mnuContext , plugin ) ;
mnuEditors = tabs . getElement ( "mnuEditors" ) ;
var div , label ;
div = menus . addItemToMenu ( mnuEditors , new ui . divider ( ) , 1000000 , plugin ) ;
label = menus . addItemToMenu ( mnuEditors , new ui . item ( {
caption : "Recently Closed Tabs" ,
disabled : true
} ) , 1000001 , plugin ) ;
menuClosedItems . hide = function ( ) { div . hide ( ) ; label . hide ( ) ; } ;
menuClosedItems . show = function ( ) { div . show ( ) ; label . show ( ) ; } ;
menuClosedItems . hide ( ) ;
// Other Hooks
tabs . on ( "paneCreate" , function ( e ) {
var pane = e . pane . aml ;
pane . on ( "contextmenu" , function ( e ) {
if ( e . currentTarget ) {
mnuContext . $tab = e . currentTarget . tagName == "page"
? e . currentTarget . cloud9tab : null ;
mnuContext . $pane = ( mnuContext . $tab || 0 ) . pane
|| e . currentTarget . cloud9pane ;
}
if ( ui . isChildOf ( pane . $buttons , e . htmlEvent . target , true ) ) {
mnuContext . display ( e . x , e . y ) ;
return false ;
}
} ) ;
var meta = e . pane . meta ;
if ( ! meta . accessList )
meta . accessList = [ ] ;
if ( ! meta . accessList . toJson )
meta . accessList . toJson = accessListToJson ;
2017-02-19 12:00:58 +00:00
} , plugin ) ;
2017-01-06 10:47:08 +00:00
//@todo store the stack for availability after reload
tabs . on ( "tabBeforeClose" , function ( e ) {
var tab = e . tab ;
var event = e . htmlEvent || { } ;
// Shift = close all
if ( event . shiftKey ) {
closealltabs ( ) ;
return false ;
}
// Alt/ Option = close all but this
else if ( event . altKey ) {
closeallbutme ( tab ) ;
return false ;
}
2017-02-19 12:00:58 +00:00
} , plugin ) ;
2017-01-06 10:47:08 +00:00
tabs . on ( "tabAfterClose" , function ( e ) {
// Hack to force focus on the right pane
var accessList = e . tab . pane . meta . accessList ;
if ( tabs . focussedTab == e . tab && accessList [ 1 ] )
e . tab . pane . aml . nextTabInLine = accessList [ 1 ] . aml ;
2017-02-19 12:00:58 +00:00
} , plugin ) ;
2017-01-06 10:47:08 +00:00
tabs . on ( "tabBeforeReparent" , function ( e ) {
// Move to new access list
var lastList = e . lastPane . meta . accessList ;
var accessList = e . tab . pane . meta . accessList ;
lastList . splice ( lastList . indexOf ( e . tab ) , 1 ) ;
if ( e . tab == tabs . focussedTab )
accessList . unshift ( e . tab ) ;
else
accessList . push ( e . tab ) ;
// Hack to force focus on the right pane
if ( tabs . focussedTab == e . tab && lastList [ 0 ] )
e . lastPane . aml . nextTabInLine = lastList [ 0 ] . aml ;
2017-02-19 12:00:58 +00:00
} , plugin ) ;
2017-01-06 10:47:08 +00:00
tabs . on ( "tabAfterClose" , function ( e ) {
var tab = e . tab ;
if ( tab . document . meta . preview )
return ;
addTabToClosedMenu ( tab ) ;
tab . pane . meta . accessList . remove ( tab ) ;
paneList . remove ( tab ) ;
2017-02-19 12:00:58 +00:00
} , plugin ) ;
2017-01-06 10:47:08 +00:00
tabs . on ( "tabCreate" , function ( e ) {
var tab = e . tab ;
if ( tab . title ) {
// @todo candidate for optimization using a hash
var path = tab . path || tab . editorType ;
for ( var i = menuClosedItems . length - 1 ; i >= 0 ; i -- ) {
if ( menuClosedItems [ i ] . path == path ) {
menuClosedItems . splice ( i , 1 ) [ 0 ] . destroy ( true , true ) ;
if ( ! menuClosedItems . length )
menuClosedItems . hide ( ) ;
}
}
}
if ( tab . document . meta . preview )
return ;
var accessList = tab . pane . meta . accessList ;
var idx ;
if ( accessList . indexOf ( tab ) == - 1 ) {
idx = accessList . indexOf ( tab . name ) ;
if ( idx == - 1 ) { //Load accesslist from index
if ( tab == tabs . focussedTab )
accessList . unshift ( tab ) ;
else
accessList . push ( tab ) ; //splice(1, 0, tab);
}
else
accessList [ idx ] = tab ;
}
if ( paneList . indexOf ( tab ) == - 1 ) {
idx = paneList . indexOf ( tab . name ) ;
if ( idx == - 1 ) { //Load paneList from index
if ( tab . isActive ( ) )
addToPaneList ( tab ) ;
}
else
paneList [ idx ] = tab ;
}
2017-02-19 12:00:58 +00:00
} , plugin ) ;
2017-01-06 10:47:08 +00:00
tabs . on ( "focusSync" , function ( e ) {
var tab = e . tab ;
if ( ! tab . loaded ) return ;
if ( ! cycleKeyPressed ) {
var accessList = tab . pane . meta . accessList ;
accessList . remove ( tab ) ;
accessList . unshift ( tab ) ;
accessList . changed = true ;
addToPaneList ( tab , true ) ;
paneList . changed = true ;
settings . save ( ) ;
}
// @todo panel switch
if ( tree . area && tree . area . activePanel == "tree"
&& settings . getBool ( 'user/general/@revealfile' ) ) {
revealtab ( tab , true ) ;
}
2017-02-19 12:00:58 +00:00
} , plugin ) ;
2017-01-06 10:47:08 +00:00
tabs . on ( "tabAfterActivate" , function ( e ) {
var tab = e . tab ;
if ( tab == tabs . focussedTab || ! tab . loaded )
return ;
if ( ! cycleKeyPressed ) {
var accessList = tab . pane . meta . accessList ;
accessList . remove ( tab ) ;
accessList . splice ( 1 , 0 , tab ) ;
accessList . changed = true ;
addToPaneList ( tab , 2 ) ;
paneList . changed = true ;
settings . save ( ) ;
}
2017-02-19 12:00:58 +00:00
} , plugin ) ;
2017-01-06 10:47:08 +00:00
apf . addEventListener ( "keydown" , function ( eInfo ) {
if ( eInfo . keyCode == 17 || eInfo . keyCode == 18 ) {
cycleKeyPressed = true ;
}
} ) ;
apf . addEventListener ( "keyup" , function ( eInfo ) {
if ( eInfo . keyCode == 17 || eInfo . keyCode == 18 ) {
cycleKeyPressed = false ;
if ( dirtyNextTab ) {
var tab = tabs . focussedTab ;
var accessList = tab . pane . meta . accessList ;
2017-02-19 15:01:52 +00:00
if ( accessList [ 0 ] != tab ) {
2017-01-06 10:47:08 +00:00
accessList . remove ( tab ) ;
accessList . unshift ( tab ) ;
accessList . changed = true ;
settings . save ( ) ;
}
dirtyNextTab = false ;
}
if ( dirtyNextPane ) {
accessedPane = 0 ;
var tab = tabs . focussedTab ;
if ( paneList [ accessedPane ] != tab && tab ) {
paneList . remove ( tab ) ;
paneList . unshift ( tab ) ;
paneList . changed = true ;
settings . save ( ) ;
}
2017-02-19 15:01:52 +00:00
dirtyNextPane = false ;
2017-01-06 10:47:08 +00:00
}
}
} ) ;
// tabs.addEventListener("aftersavedialogcancel", function(e) {
// if (!changedTabs)
// return;
// var i, l, tab;
// for (i = 0, l = changedTabs.length; i < l; i++) {
// tab = changedTabs[i];
// tab.removeEventListener("aftersavedialogclosed", arguments.callee);
// }
// });
2017-02-06 17:46:50 +00:00
createLayoutMenus ( ) ;
2017-01-06 10:47:08 +00:00
}
/***** Methods *****/
function addToPaneList ( tab , first ) {
var pane = tab . pane , found ;
paneList . every ( function ( tab ) {
if ( tab && tab . pane && tab . pane == pane ) {
found = tab ;
return false ;
}
return true ;
} ) ;
if ( found )
paneList . remove ( found ) ;
if ( first == 2 )
paneList . splice ( 1 , 0 , tab ) ;
else if ( first )
paneList . unshift ( tab ) ;
else
paneList . push ( tab ) ;
}
function accessListToJson ( ) {
var list = [ ] ;
this . forEach ( function ( tab , i ) {
if ( tab && tab . name )
list . push ( tab . name ) ;
} ) ;
return list ;
}
function clonetab ( tab ) {
if ( ! tab )
tab = mnuContext . $tab || tabs . focussedTab ;
var pane ;
tabs . getTabs ( ) . every ( function ( tab ) {
if ( tab . document . meta . cloned ) {
pane = tab . pane ;
return false ;
}
return true ;
} ) ;
if ( ! pane || pane == tab . pane )
pane = tab . pane . hsplit ( true ) ;
tabs . clone ( tab , pane , function ( err , tab ) {
} ) ;
}
function closetab ( tab ) {
if ( ! tab )
tab = mnuContext . $tab || tabs . focussedTab ;
var pages = tabs . getTabs ( ) ;
var isLast = pages [ pages . length - 1 ] == tab ;
tab . close ( ) ;
tabs . resizePanes ( isLast ) ;
return false ;
}
function closealltabs ( callback ) {
callback = typeof callback == "function" ? callback : null ;
changedTabs = [ ] ;
unchangedTabs = [ ] ;
var pages = tabs . getTabs ( ) ;
for ( var i = 0 , l = pages . length ; i < l ; i ++ ) {
closepage ( pages [ i ] , callback ) ;
}
checkTabRender ( callback ) ;
}
function closeallbutme ( me , pages , callback ) {
if ( ! me ) {
me = mnuContext . $tab || tabs . focussedTab ;
}
changedTabs = [ ] ;
unchangedTabs = [ ] ;
if ( ! pages || ! ( pages instanceof Array ) ) {
var container = me && me . aml && me . aml . parentNode || tabs . container ;
pages = tabs . getTabs ( container ) ;
}
var tab ;
for ( var i = 0 , l = pages . length ; i < l ; i ++ ) {
tab = pages [ i ] ;
if ( tab !== me )
closepage ( tab , callback ) ;
}
tabs . resizePanes ( ) ;
checkTabRender ( callback ) ;
}
function closepage ( tab , callback ) {
var doc = tab . document ;
if ( doc . changed && ( ! doc . meta . newfile || doc . value ) )
changedTabs . push ( tab ) ;
else
unchangedTabs . push ( tab ) ;
}
function checkTabRender ( callback ) {
save . saveAllInteractive ( changedTabs , function ( result ) {
if ( result != save . CANCEL ) {
changedTabs . forEach ( function ( tab ) {
tab . unload ( ) ;
} ) ;
closeUnchangedTabs ( done ) ;
}
else
done ( ) ;
} ) ;
function done ( ) {
if ( callback ) callback ( ) ;
// todo dialog calls this twice when selecting no with changed tab
setTimeout ( function ( ) {
changedTabs = [ ] ;
unchangedTabs = [ ] ;
} ) ;
}
}
function closeUnchangedTabs ( callback ) {
var tab ;
for ( var i = 0 , l = unchangedTabs . length ; i < l ; i ++ ) {
tab = unchangedTabs [ i ] ;
tab . close ( true ) ;
}
if ( callback )
callback ( ) ;
}
function closealltotheright ( tab ) {
if ( ! tab )
tab = mnuContext . $tab || tabs . focussedTab ;
var pages = tab . pane . getTabs ( ) ;
var currIdx = pages . indexOf ( tab ) ;
closeallbutme ( tab , pages . slice ( currIdx ) ) ;
}
function closealltotheleft ( tab ) {
if ( ! tab )
tab = mnuContext . $tab || tabs . focussedTab ;
var pages = tab . pane . getTabs ( ) ;
var currIdx = pages . indexOf ( tab ) ;
closeallbutme ( tab , pages . slice ( 0 , currIdx ) ) ;
}
2017-02-19 15:01:52 +00:00
function nexttab ( dir , keepOrder ) {
2017-01-06 10:47:08 +00:00
if ( tabs . getTabs ( ) . length === 1 )
return ;
var tab = tabs . focussedTab ;
var accessList = tab . pane . meta . accessList ;
2017-02-19 15:01:52 +00:00
var index = accessList . indexOf ( tab ) ;
index += dir || 1 ;
if ( index >= accessList . length )
index = 0 ;
else if ( index < 0 )
index = accessList . length - 1 ;
var next = accessList [ index ] ;
2017-01-06 10:47:08 +00:00
if ( typeof next != "object" || ! next . pane . visible )
2017-02-19 15:01:52 +00:00
return nexttab ( dir , keepOrder ) ;
if ( keepOrder && cycleKeyPressed == false ) {
cycleKeyPressed = true ;
tabs . focusTab ( next , null , true ) ;
cycleKeyPressed = false ;
} else {
tabs . focusTab ( next , null , true ) ;
}
dirtyNextTab = ! keepOrder ;
2017-01-06 10:47:08 +00:00
}
2017-02-19 15:01:52 +00:00
function previoustab ( dir , keepOrder ) {
nexttab ( dir || - 1 , keepOrder )
2017-01-06 10:47:08 +00:00
}
function nextpane ( ) {
return $nextPane ( 1 ) ;
}
function previouspane ( ) {
return $nextPane ( - 1 ) ;
}
function $nextPane ( dir ) {
if ( tabs . getPanes ( ) . length === 1 )
return ;
var l = paneList . length ;
for ( var i = 1 ; i <= l ; i ++ ) {
var index = ( accessedPane + dir * i ) % l ;
var next = paneList [ index ] ;
if ( typeof next != "object" || ! next . pane . visible )
continue ;
if ( next . pane . activeTab == tabs . focussedTab ) {
console . error ( "error in panelist" ) ;
continue ;
}
accessedPane = index ;
tabs . focusTab ( next . pane . activeTab , null , true ) ;
dirtyNextPane = true ;
return next . pane ;
}
}
function gotopaneleft ( ) {
return $goToPane ( "left" ) ;
}
function gotopaneright ( ) {
return $goToPane ( "right" ) ;
}
function gotopanedown ( ) {
return $goToPane ( "down" ) ;
}
function gotopaneup ( ) {
return $goToPane ( "up" ) ;
}
function $goToPane ( direction ) {
var newPane = findPaneToGoTo ( direction ) ;
if ( ! newPane ) return ;
var activeTab = newPane . activeTab ;
tabs . focusTab ( activeTab ) ;
}
function getPaneDimensions ( pane ) {
var element = pane . container ;
var size = getElementSize ( element ) ;
var dimensions = {
x : getElementOffset ( element , "Left" ) ,
y : getElementOffset ( element , "Top" ) ,
width : size . width ,
height : size . height
} ;
return dimensions ;
}
function getElementOffset ( element , type ) {
var offset = 0 ;
do {
if ( ! isNaN ( element [ 'offset' + type ] ) )
{
offset += element [ 'offset' + type ] ;
}
} while ( element = element . offsetParent ) ;
return offset ;
}
function getElementSize ( element ) {
var computedStyle = window . getComputedStyle ( element ) ;
return {
width : parseInt ( computedStyle . width , 10 ) ,
height : parseInt ( computedStyle . height , 10 ) ,
} ;
}
/ * * F o r e a c h d i r e c t i o n
2017-02-06 17:46:50 +00:00
* Exclude all panes not in the direction of this one
* Exclude all panes that don ' t intersect on the other axis
* Choose the closest pane
* In case of tie choose the pane to the furthest left or top .
* * /
2017-01-06 10:47:08 +00:00
function findBoxToGoTo ( boxes , currentBox , direction ) {
var possibleBoxes = [ ] ;
switch ( direction ) {
case "left" :
possibleBoxes = boxes
. filter ( function ( box ) { return box . x < currentBox . x ; } )
. filter ( areBoxesInLineVertically . bind ( null , currentBox ) ) ;
if ( ! possibleBoxes . length ) return null ;
var chosenBox = possibleBoxes . reduce ( function ( prev , cur ) {
if ( ! prev || cur . x > prev . x ) return cur ;
if ( cur . x == prev . x && cur . y < prev . y ) return cur ;
return prev ;
} ) ;
return chosenBox ;
break ;
case "right" :
possibleBoxes = boxes
. filter ( function ( box ) { return box . x > currentBox . x ; } )
. filter ( areBoxesInLineVertically . bind ( null , currentBox ) ) ;
if ( ! possibleBoxes . length ) return null ;
var chosenBox = possibleBoxes . reduce ( function ( prev , cur ) {
if ( ! prev || cur . x < prev . x ) return cur ;
if ( cur . x == prev . x && cur . y < prev . y ) return cur ;
return prev ;
} ) ;
return chosenBox ;
break ;
case "up" :
possibleBoxes = boxes
. filter ( function ( box ) { return box . y < currentBox . y ; } )
. filter ( areBoxesInLineHorizontally . bind ( null , currentBox ) ) ;
if ( ! possibleBoxes . length ) return null ;
var chosenBox = possibleBoxes . reduce ( function ( prev , cur ) {
if ( ! prev || cur . y > prev . y ) return cur ;
if ( cur . y == prev . y && cur . x < prev . x ) return cur ;
return prev ;
} ) ;
return chosenBox ;
break ;
case "down" :
possibleBoxes = boxes
. filter ( function ( box ) { return box . y > currentBox . y ; } )
. filter ( areBoxesInLineHorizontally . bind ( null , currentBox ) ) ;
if ( ! possibleBoxes . length ) return null ;
var chosenBox = possibleBoxes . reduce ( function ( prev , cur ) {
if ( ! prev || cur . y < prev . y ) return cur ;
if ( cur . y == prev . y && cur . x < prev . x ) return cur ;
return prev ;
} ) ;
return chosenBox ;
break ;
}
}
function areBoxesInLineVertically ( box1 , box2 ) {
return ! ( box1 . y + box1 . height < box2 . y || box2 . y + box2 . height < box1 . y ) ;
}
function areBoxesInLineHorizontally ( box1 , box2 ) {
return ! ( box1 . x + box1 . width < box2 . x || box2 . x + box2 . width < box1 . x ) ;
}
function findPaneToGoTo ( direction ) {
var panes = tabs . getPanes ( ) ;
if ( ! tabs . focussed || ! tabs . focussedTab )
return ;
var currentPane = tabs . focussedTab . pane ;
if ( ! currentPane ) return ;
var boxes = panes . map ( function ( pane ) {
return getPaneDimensions ( pane ) ;
} ) ;
var currentBox = getPaneDimensions ( currentPane ) ;
var newBox = findBoxToGoTo ( boxes , currentBox , direction ) ;
if ( ! newBox ) return ;
var newPane = null ;
panes . forEach ( function ( pane ) {
var paneDimensions = getPaneDimensions ( pane ) ;
if ( paneDimensions . x == newBox . x && paneDimensions . y == newBox . y ) {
newPane = pane ;
}
} ) ;
return newPane ;
}
function gototabright ( opts ) {
return cycleTab ( "right" , opts ) ;
}
function gototableft ( opts ) {
return cycleTab ( "left" , opts ) ;
}
function cycleTab ( dir , opts ) {
var curr = tabs . focussedTab ;
var pages = curr && curr . pane . getTabs ( ) ;
if ( ! pages || pages . length == 1 )
return ;
if ( opts && opts . editorType ) {
pages = pages . filter ( function ( p ) {
return p . editorType == opts . editorType ;
} ) ;
}
var currIdx = pages . indexOf ( curr ) ;
var start = currIdx ;
var tab ;
do {
var idx = currIdx ;
switch ( dir ) {
case "right" : idx ++ ; break ;
case "left" : idx -- ; break ;
case "first" : idx = 0 ; break ;
case "last" : idx = pages . length - 1 ; break ;
default : idx -- ;
}
if ( idx < 0 )
idx = pages . length - 1 ;
if ( idx > pages . length - 1 )
idx = 0 ;
// No pages found that can be focussed
if ( start == idx )
return ;
tab = pages [ idx ] ;
}
while ( ! tab . pane . visible ) ;
if ( tab . pane . visible )
tabs . focusTab ( tab , null , true ) ;
return false ;
}
function movetabright ( ) { hmoveTab ( "right" ) ; }
function movetableft ( ) { hmoveTab ( "left" ) ; }
function movetabup ( ) { vmoveTab ( "up" ) ; }
function movetabdown ( ) { vmoveTab ( "down" ) ; }
function hmoveTab ( dir ) {
var bRight = dir == "right" ;
var tab = tabs . focussedTab ;
if ( ! tab )
return ;
// Tabs within the current pane
var pages = tab . pane . getTabs ( ) ;
// Get new index
var idx = pages . indexOf ( tab ) + ( bRight ? 2 : - 1 ) ;
// Before current pane
if ( idx < 0 || idx > pages . length ) {
tab . pane . moveTabToSplit ( tab , dir ) ;
}
// In current pane
else {
tab . attachTo ( tab . pane , pages [ idx ] , null , true ) ;
}
tabs . focusTab ( tab ) ;
return false ;
}
function vmoveTab ( dir ) {
var tab = tabs . focussedTab ;
if ( ! tab )
return ;
tab . pane . moveTabToSplit ( tab , dir ) ;
tabs . focusTab ( tab ) ;
return false ;
}
function tab1 ( ) { return showTab ( 1 ) ; }
function tab2 ( ) { return showTab ( 2 ) ; }
function tab3 ( ) { return showTab ( 3 ) ; }
function tab4 ( ) { return showTab ( 4 ) ; }
function tab5 ( ) { return showTab ( 5 ) ; }
function tab6 ( ) { return showTab ( 6 ) ; }
function tab7 ( ) { return showTab ( 7 ) ; }
function tab8 ( ) { return showTab ( 8 ) ; }
function tab9 ( ) { return showTab ( 9 ) ; }
function tab0 ( ) { return showTab ( 10 ) ; }
function showTab ( idx ) {
// our indexes are 0 based an the number coming in is 1 based
var pages = [ ] ;
tabs . getPanes ( ) . forEach ( function ( pane ) {
pages = pages . concat ( pane . getTabs ( ) ) ;
} ) ;
pages = pages . filter ( function ( tab ) {
return tab . title ;
} ) ;
var tab = pages [ idx - 1 ] ;
if ( ! tab )
return false ;
tabs . focusTab ( tab , null , true ) ;
return false ;
}
/ * *
* Scrolls to the selected pane ' s file path in the "Project Files" tree
*
* Works by Finding the node related to the active pane in the tree , and
* unfolds its parent folders until the node can be reached by an xpath
* selector and focused , to finally scroll to the selected node .
* /
function revealtab ( tab , noFocus ) {
if ( ! tab || tab . command )
tab = tabs . focussedTab ;
if ( ! tab )
return false ;
// Tell other extensions to exit their fullscreen mode (for ex. Zen)
// so this operation is visible
// ide.dispatchEvent("exitfullscreen");
revealInTree ( tab , noFocus ) ;
}
function revealInTree ( tab , noFocus ) {
panels . activate ( "tree" ) ;
var path = tab . path || tab . relatedPath ;
if ( path )
done ( null , path ) ;
else if ( tab . editor . getPathAsync )
tab . editor . getPathAsync ( done ) ;
if ( ! noFocus )
tree . focus ( ) ;
function done ( err , path ) {
if ( err || ! path )
return console . error ( err ) ;
tree . expand ( path , function ( err ) {
if ( ! err )
tree . select ( path ) ;
tree . scrollToSelection ( ) ;
} ) ;
}
}
function canTabBeRemoved ( pane , min ) {
if ( ! pane || pane . getTabs ( ) . length > ( min || 0 ) )
return false ;
var containers = tabs . containers ;
for ( var i = 0 ; i < containers . length ; i ++ ) {
if ( ui . isChildOf ( containers [ i ] , pane . aml ) ) {
return containers [ i ]
. getElementsByTagNameNS ( apf . ns . aml , "tab" ) . length > 1 ;
}
}
return false ;
}
function closepane ( tab , pane ) {
if ( ! tab )
tab = tabs . focussedTab ;
if ( ! pane )
pane = tab . pane ;
if ( ! pane ) return ;
var pages = pane . getTabs ( ) ;
if ( ! pages . length ) {
if ( canTabBeRemoved ( pane ) )
pane . unload ( ) ;
return ;
}
changedTabs = [ ] ;
unchangedTabs = [ ] ;
// Ignore closing tabs
menuClosedItems . ignore = true ;
// Keep information to restore pane set
var state = [ ] ;
var type = pane . aml . parentNode . localName ;
var nodes = pane . aml . parentNode . childNodes . filter ( function ( p ) {
return p . localName != "splitter" ;
} ) ;
state . title = pages . length + " Tabs" ;
state . type = type == "vsplitbox" ? "vsplit" : "hsplit" ;
state . far = nodes . indexOf ( pane . aml ) == 1 ;
state . sibling = nodes [ state . far ? 0 : 1 ] ;
state . getState = function ( ) { return state ; } ;
state . restore = $restoreTabGroup ;
state . paneName = pane . name ;
state . document = { meta : { } } ;
// Close pages
pages . forEach ( function ( tab ) {
state . push ( tab . getState ( ) ) ;
closepage ( tab ) ;
} ) ;
tabs . resizePanes ( ) ;
checkTabRender ( function ( ) {
if ( canTabBeRemoved ( pane ) )
pane . unload ( ) ;
// Stop ignoring closing tabs
menuClosedItems . ignore = false ;
// @todo there should probably be some check here
addTabToClosedMenu ( state ) ;
} ) ;
}
function $restoreTabGroup ( state ) {
// pane was not being used. Why?
// var pane = state.sibling;
// if (pane && pane.cloud9pane)
// pane = pane.cloud9pane.aml;
var pane = tabs . findPane ( state . paneName ) || { } ;
var oldpane = state . pane ;
var newpane = oldpane . getTabs ( ) . length === 0
? oldpane
: oldpane [ state . type ] ( state . far , null , pane . aml ) ;
state . forEach ( function ( s ) {
s . pane = newpane ;
tabs . open ( s , function ( ) { } ) ;
} ) ;
}
function hsplit ( tab , pane ) {
if ( ! tab )
tab = tabs . focussedTab ;
if ( tab )
pane = tab . pane ;
var newpane = pane . hsplit ( true ) ;
if ( pane . getTabs ( ) . length > 1 )
tab . attachTo ( newpane ) ;
}
function vsplit ( tab , pane ) {
if ( ! tab )
tab = tabs . focussedTab ;
if ( tab )
pane = tab . pane ;
var newpane = pane . vsplit ( true ) ;
if ( pane . getTabs ( ) . length > 1 )
tab . attachTo ( newpane ) ;
}
function nosplit ( ) {
var panes = tabs . getPanes ( tabs . container ) ;
var first = panes [ 0 ] ;
for ( var pane , i = 1 , li = panes . length ; i < li ; i ++ ) {
var pages = ( pane = panes [ i ] ) . getTabs ( ) ;
for ( var j = 0 , lj = pages . length ; j < lj ; j ++ ) {
pages [ j ] . attachTo ( first , null , true ) ;
}
pane . unload ( ) ;
}
}
function twovsplit ( hsplit ) {
var panes = tabs . getPanes ( tabs . container ) ;
// We're already in a two vsplit
if ( panes . length == 2 && panes [ 0 ] . aml . parentNode . localName
== ( hsplit ? "hsplitbox" : "vsplitbox" ) )
return panes ;
// Split the only pane there is
if ( panes . length == 1 ) {
var newtab = panes [ 0 ] [ hsplit ? "hsplit" : "vsplit" ] ( true ) ;
return [ panes [ 0 ] , newtab ] ;
}
var c = tabs . containers [ 0 ] . firstChild . childNodes . filter ( function ( f ) {
return f . localName != "splitter" ;
} ) ;
// var left = c[0].getElementsByTagNameNS(apf.ns.aml, "tab");
var right = c [ 1 ] . getElementsByTagNameNS ( apf . ns . aml , "tab" ) ;
for ( var i = 1 , l = panes . length ; i < l ; i ++ ) {
panes [ i ] . unload ( ) ;
}
var newtab = panes [ 0 ] [ hsplit ? "hsplit" : "vsplit" ] ( true ) ;
right . forEach ( function ( tab ) {
if ( tab . cloud9tab )
tab . cloud9tab . attachTo ( newtab , null , true ) ;
} ) ;
return [ panes [ 0 ] , newtab ] ;
}
function twohsplit ( ) {
return twovsplit ( true ) ;
}
function foursplit ( ) {
var panes = twohsplit ( ) ;
panes [ 0 ] . vsplit ( true ) ;
panes [ 1 ] . vsplit ( true ) ;
}
function threeleft ( ) {
var panes = twohsplit ( ) ;
panes [ 0 ] . vsplit ( true ) ;
}
function threeright ( ) {
var panes = twohsplit ( ) ;
panes [ 1 ] . vsplit ( true ) ;
}
function checkReopenedTab ( e ) {
var tab = e . tab ;
if ( ! tab . path )
return ;
fs . stat ( tab . path , function ( err , stat ) {
if ( err ) return ;
// @todo this won't work well on windows, because
// there is a 20s period in which the mtime is
// the same. The solution would be to have a
// way to compare the saved document to the
// loaded document that created the state
if ( tab . document . meta . timestamp < stat . mtime ) {
var doc = tab . document ;
question ( "File Changed" ,
tab . path + " has been changed on disk." ,
"Would you like to reload this file?" ,
function ( ) {
tabs . reload ( tab , function ( ) { } ) ;
} ,
function ( ) {
// Set to changed
doc . undoManager . bookmark ( - 2 ) ;
} ,
{ merge : false , all : false }
) ;
}
} ) ;
}
// Record the last 10 closed tabs or pane sets
function addTabToClosedMenu ( tab ) {
if ( menuClosedItems . ignore ) return ;
if ( tab . document . meta . preview || tab . document . meta . cloned )
return ;
// Record state
var state = tab . getState ( ) ;
var restore = tab . restore ;
var path = tab . path || tab . editorType ;
if ( ! restore ) {
for ( var i = menuClosedItems . length - 1 ; i >= 0 ; i -- ) {
if ( menuClosedItems [ i ] . path == path ) {
menuClosedItems . splice ( i , 1 ) [ 0 ] . destroy ( true , true ) ;
}
}
}
// Create menu item
var item = new ui . item ( {
caption : tab . title ,
path : path ,
style : "padding-left:35px" ,
onclick : function ( e ) {
// Update State
state . active = true ;
state . pane = this . parentNode . pane ;
tabs . on ( "open" , checkReopenedTab ) ;
// Open pane
restore
? restore ( state )
: tabs . open ( state , function ( ) { } ) ;
tabs . off ( "open" , checkReopenedTab ) ;
// Remove pane from menu
menuClosedItems . remove ( item ) ;
item . destroy ( true , true ) ;
// Clear label and divider if there are no items
if ( menuClosedItems . length === 0 )
menuClosedItems . hide ( ) ;
}
} ) ;
// TODO: passing path to item doesn't work since apf adds it only when menu is shown
item . path = path ;
// Add item to menu
menuClosedItems . push ( item ) ;
var index = menuClosedItems . index = ( menuClosedItems . index || 0 ) + 1 ;
menus . addItemToMenu ( mnuEditors , item , 2000000 - index , false ) ;
// Show label and divider
menuClosedItems . show ( ) ;
// Remove excess menu item
if ( menuClosedItems . length > 10 )
menuClosedItems . shift ( ) . destroy ( true , true ) ;
tab = null ;
}
function updateTabMenu ( force ) {
// Approximating order
var pages = [ ] ;
tabs . getPanes ( ) . forEach ( function ( pane ) {
pages = pages . concat ( pane . getTabs ( ) ) ;
} ) ;
var length = Math . min ( 10 , pages . length ) ;
var start = 1000 ;
// Destroy all items
menuItems . forEach ( function ( item ) {
item . destroy ( true , true ) ;
} ) ;
menuItems = [ ] ;
if ( ! pages . length )
return ;
var mnu , tab ;
// Create new divider
menus . addItemToMenu ( mnuTabs , mnu = new ui . divider ( ) , start , false ) ;
menuItems . push ( mnu ) ;
// Create new items
for ( var i = 0 ; i < length ; i ++ ) {
tab = pages [ i ] ;
if ( ! tab . title ) continue ;
menus . addItemToMenu ( mnuTabs , mnu = new ui . item ( {
caption : tab . title . replace ( /[/]/g , "\u2044" ) ,
relPage : tab ,
command : "tab" + ( i == 9 ? 0 : i + 1 )
} ) , start + i + 1 , false ) ;
menuItems . push ( mnu ) ;
}
if ( pages . length > length ) {
menus . addItemToMenu ( mnuTabs , mnu = new ui . item ( {
caption : "More..." ,
onclick : function ( ) {
commands . exec ( "toggleOpenfiles" , null , { forceOpen : true } ) ;
}
} ) , start + length + 1 , false ) ;
menuItems . push ( mnu ) ;
}
tab = pages = null ;
}
function reopenLastTab ( ) {
var item = menuClosedItems [ menuClosedItems . length - 1 ] ;
if ( item )
item . getAttribute ( "onclick" ) . call ( item ) ;
}
2017-02-06 17:46:50 +00:00
function createLayoutMenus ( ) {
var LAYOUT _MENU _PATH = "Window/Saved Layouts/" ;
var SAVED _LAYOUTS _PATH = "/.c9/saved-layouts/" ;
commands . addCommand ( {
name : "savePaneLayout" ,
group : "Window" ,
bindKey : { } ,
exec : function ( editor , args ) {
var state = tabs . getState ( null , true ) ;
var stateName = prompt ( "Name your layout" , getAutoSaveName ( ) ) ;
if ( ! stateName )
return ;
var sanitizedStateName = stateName . trim ( ) . replace ( /[\\\/:\r\n~]|\.\./g , "-" ) + ".tabstate" ;
fs . writeFile ( SAVED _LAYOUTS _PATH + sanitizedStateName , JSON . stringify ( state , null , "\t" ) , function ( err ) {
if ( err ) {
return alert ( err ) ;
}
} ) ;
}
} , plugin ) ;
commands . addCommand ( {
name : "savePaneLayoutAndCloseTabs" ,
group : "Window" ,
bindKey : { } ,
exec : function ( editor , args ) {
commands . exec ( "savePaneLayout" ) ;
tabs . setState ( null , function ( ) { } ) ;
}
} , plugin ) ;
// menus.insert
menus . addItemByPath ( LAYOUT _MENU _PATH , new ui . menu ( {
"onprop.visible" : function ( e ) {
if ( e . value ) {
rebuildLayoutMenu ( ) ;
fs . readdir ( SAVED _LAYOUTS _PATH , function ( err , files ) {
rebuildLayoutMenu ( err , files ) ;
} ) ;
}
} ,
"onitemclick" : function ( e ) {
var stat = e . relatedNode && e . relatedNode . value ;
if ( stat && stat . name ) {
fs . readFile ( SAVED _LAYOUTS _PATH + stat . name , function ( err , contents ) {
if ( err ) return alert ( err ) ;
try {
contents = JSON . parse ( contents ) ;
}
catch ( e ) {
return alert ( e ) ;
}
tabs . setState ( null , function ( ) { } ) ;
tabs . setState ( contents , function ( err ) {
if ( err ) return alert ( err ) ;
} ) ;
} ) ;
}
}
} ) , 10050 , plugin ) ;
function getAutoSaveName ( ) {
return ( new Date ( ) ) . toLocaleString ( ) + " [" + tabs . getTabs ( ) . length + " tabs]" ;
}
function rebuildLayoutMenu ( err , stats ) {
menus . remove ( LAYOUT _MENU _PATH ) ;
var c = 0 ;
menus . addItemByPath ( LAYOUT _MENU _PATH + "Save..." , new ui . item ( {
command : "savePaneLayout"
} ) , c += 100 , plugin ) ;
menus . addItemByPath ( LAYOUT _MENU _PATH + "Save And Close All..." , new ui . item ( {
command : "savePaneLayoutAndCloseTabs"
} ) , c += 100 , plugin ) ;
menus . addItemByPath ( LAYOUT _MENU _PATH + "~" , new ui . divider ( {
} ) , c += 100 , plugin ) ;
menus . addItemByPath ( LAYOUT _MENU _PATH + "Show Saved Layouts in File Tree" , new ui . item ( {
onclick : function ( ) {
revealInTree ( { path : SAVED _LAYOUTS _PATH } ) ;
}
} ) , c += 100 , plugin ) ;
menus . addItemByPath ( LAYOUT _MENU _PATH + "~" , new ui . divider ( {
} ) , c += 100 , plugin ) ;
if ( err ) {
if ( err . code == "ENOENT" )
return ;
return menus . addItemByPath ( LAYOUT _MENU _PATH + "Error loading saved layouts" , new ui . item ( {
disabled : true ,
} ) , c += 100 , plugin ) ;
}
else if ( ! stats ) {
return menus . addItemByPath ( LAYOUT _MENU _PATH + "loading..." , new ui . item ( {
disabled : true ,
} ) , c += 100 , plugin ) ;
}
for ( var i = 0 ; i < stats . length ; i ++ ) {
var stat = stats [ i ] ;
var caption = stat . name . replace ( /.tabstate$/ , "" ) ;
menus . addItemByPath ( LAYOUT _MENU _PATH + caption , new ui . item ( {
value : stat ,
} ) , c += 100 , plugin ) ;
}
}
}
2017-01-06 10:47:08 +00:00
/***** Lifecycle *****/
plugin . on ( "load" , function ( ) {
load ( ) ;
} ) ;
plugin . on ( "enable" , function ( ) {
} ) ;
plugin . on ( "disable" , function ( ) {
} ) ;
plugin . on ( "unload" , function ( ) {
menuItems . forEach ( function ( item ) {
item . destroy ( true , true ) ;
} ) ;
menuItems = [ ] ;
menuClosedItems . forEach ( function ( item ) {
item . destroy ( true , true ) ;
} ) ;
menuClosedItems . length = 0 ; // = [];
mnuContext = null ;
mnuEditors = null ;
mnuTabs = null ;
paneList = null ;
accessedPane = null ;
cycleKeyPressed = null ;
changedTabs = null ;
unchangedTabs = null ;
dirtyNextTab = null ;
dirtyNextPane = null ;
loaded = false ;
} ) ;
/***** Register and define API *****/
/ * *
* Draws the file tree
* @ event afterfilesave Fires after a file is saved
* @ param { Object } e
* node { XMLNode } description
* oldpath { String } description
* * /
plugin . freezePublicAPI ( {
/ * *
*
* /
get contextMenu ( ) { return mnuContext ; } ,
/ * *
*
* /
clonetab : clonetab ,
/ * *
*
* /
closetab : closetab ,
/ * *
*
* /
closealltabs : closealltabs ,
/ * *
*
* /
closeallbutme : closeallbutme ,
/ * *
*
* /
gototabright : gototabright ,
/ * *
*
* /
gototableft : gototableft ,
/ * *
*
* /
movetabright : movetabright ,
/ * *
*
* /
movetableft : movetableft ,
/ * *
*
* /
movetabup : movetabup ,
/ * *
*
* /
movetabdown : movetabdown ,
/ * *
*
* /
tab1 : tab1 ,
/ * *
*
* /
tab2 : tab2 ,
/ * *
*
* /
tab3 : tab3 ,
/ * *
*
* /
tab4 : tab4 ,
/ * *
*
* /
tab5 : tab5 ,
/ * *
*
* /
tab6 : tab6 ,
/ * *
*
* /
tab7 : tab7 ,
/ * *
*
* /
tab8 : tab8 ,
/ * *
*
* /
tab9 : tab9 ,
/ * *
*
* /
tab0 : tab0 ,
/ * *
*
* /
revealtab : revealtab ,
/ * *
*
* /
reopenLastTab : reopenLastTab ,
/ * *
*
* /
nexttab : nexttab ,
/ * *
*
* /
previoustab : previoustab ,
/ * *
*
* /
closealltotheright : closealltotheright ,
/ * *
*
* /
closealltotheleft : closealltotheleft ,
/ * *
*
* /
closepane : closepane ,
/ * *
*
* /
hsplit : hsplit ,
/ * *
*
* /
vsplit : vsplit ,
/ * *
*
* /
nosplit : nosplit ,
/ * *
*
* /
twovsplit : twovsplit ,
/ * *
*
* /
twohsplit : twohsplit ,
/ * *
*
* /
foursplit : foursplit ,
/ * *
*
* /
threeleft : threeleft ,
/ * *
*
* /
threeright : threeright ,
/ * *
*
* /
nextpane : nextpane ,
/ * *
*
* /
previouspane : previouspane ,
/ * *
*
* /
gotopaneleft : gotopaneleft ,
/ * *
*
* /
gotopaneright : gotopaneright ,
/ * *
*
* /
gotopanedown : gotopanedown ,
/ * *
*
* /
gotopaneup : gotopaneup ,
/ * *
* @ ignore
* /
cycleTab : cycleTab
} ) ;
register ( null , {
tabbehavior : plugin
} ) ;
}
} ) ;