define ( [
'jquery' ,
'/api/config' ,
'/common/toolbar.js' ,
'json.sortify' ,
'/common/common-util.js' ,
'/common/common-hash.js' ,
'/common/common-ui-elements.js' ,
'/common/common-interface.js' ,
'/common/common-constants.js' ,
'/common/common-feedback.js' ,
'/common/inner/share.js' ,
'/common/inner/access.js' ,
'/common/inner/properties.js' ,
'/bower_components/nthen/index.js' ,
'/common/hyperscript.js' ,
'/common/proxy-manager.js' ,
'/customize/application_config.js' ,
'/customize/messages.js' ,
] , function (
$ ,
ApiConfig ,
Toolbar ,
JSONSortify ,
Util ,
Hash ,
UIElements ,
UI ,
Constants ,
Feedback ,
Share ,
Access ,
Properties ,
nThen ,
h ,
ProxyManager ,
AppConfig ,
Messages )
{
var APP = window . APP = {
editable : false ,
online : true ,
mobile : function ( ) {
if ( window . matchMedia ) { return ! window . matchMedia ( '(any-pointer:fine)' ) . matches ; }
else { return $ ( 'body' ) . width ( ) <= 600 ; }
} ,
isMac : navigator . platform === "MacIntel" ,
allowFolderUpload : File . prototype . hasOwnProperty ( "webkitRelativePath" ) ,
} ;
var stringify = function ( obj ) {
return JSONSortify ( obj ) ;
} ;
var E _OVER _LIMIT = 'E_OVER_LIMIT' ;
var ROOT = "root" ;
var ROOT _NAME = Messages . fm _rootName ;
var SEARCH = "search" ;
var SEARCH _NAME = Messages . fm _searchName ;
var TRASH = "trash" ;
var TRASH _NAME = Messages . fm _trashName ;
var FILES _DATA = Constants . storageKey ;
var FILES _DATA _NAME = Messages . fm _filesDataName ;
var TEMPLATE = "template" ;
var TEMPLATE _NAME = Messages . fm _templateName ;
var RECENT = "recent" ;
var RECENT _NAME = Messages . fm _recentPadsName ;
var OWNED = "owned" ;
var OWNED _NAME = Messages . fm _ownedPadsName ;
var TAGS = "tags" ;
var TAGS _NAME = Messages . fm _tagsName ;
var SHARED _FOLDER = 'sf' ;
var SHARED _FOLDER _NAME = Messages . fm _sharedFolderName ;
// Icons
var faFolder = 'cptools-folder' ;
var faFolderOpen = 'cptools-folder-open' ;
var faSharedFolder = 'cptools-shared-folder' ;
var faSharedFolderOpen = 'cptools-shared-folder-open' ;
var faExpandAll = 'fa-plus-square-o' ;
var faCollapseAll = 'fa-minus-square-o' ;
var faShared = 'fa-shhare-alt' ;
var faReadOnly = 'fa-eye' ;
var faPreview = 'fa-eye' ;
var faOpenInCode = 'cptools-code' ;
var faRename = 'fa-pencil' ;
var faColor = 'cptools-palette' ;
var faTrash = 'fa-trash' ;
var faCopy = 'fa-files-o' ;
var faDelete = 'cptools-destroy' ;
var faAccess = 'fa-unlock-alt' ;
var faProperties = 'fa-info-circle' ;
var faTags = 'fa-hashtag' ;
var faUploadFiles = 'cptools-file-upload' ;
var faUploadFolder = 'cptools-folder-upload' ;
var faEmpty = 'fa-trash-o' ;
var faRestore = 'fa-repeat' ;
var faShowParent = 'fa-location-arrow' ;
var faDownload = 'fa-download' ;
var $folderIcon = $ ( '<span>' , {
"class" : faFolder + " cptools cp-app-drive-icon-folder cp-app-drive-content-icon"
} ) ;
//var $folderIcon = $('<img>', {src: "/customize/images/icons/folder.svg", "class": "folder icon"});
var $folderEmptyIcon = $folderIcon . clone ( ) ;
var $folderOpenedIcon = $ ( '<span>' , { "class" : faFolderOpen + " cptools cp-app-drive-icon-folder" } ) ;
//var $folderOpenedIcon = $('<img>', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"});
var $folderOpenedEmptyIcon = $folderOpenedIcon . clone ( ) ;
var $sharedFolderIcon = $ ( '<span>' , { "class" : faSharedFolder + " cptools cp-app-drive-icon-folder" } ) ;
var $sharedFolderOpenedIcon = $ ( '<span>' , { "class" : faSharedFolderOpen + " cptools cp-app-drive-icon-folder" } ) ;
//var $upIcon = $('<span>', {"class": "fa fa-arrow-circle-up"});
var $unsortedIcon = $ ( '<span>' , { "class" : "fa fa-files-o" } ) ;
var $templateIcon = $ ( '<span>' , { "class" : "cptools cptools-template" } ) ;
var $recentIcon = $ ( '<span>' , { "class" : "fa fa-clock-o" } ) ;
var $trashIcon = $ ( '<span>' , { "class" : "fa " + faTrash } ) ;
var $trashEmptyIcon = $ ( '<span>' , { "class" : "fa fa-trash-o" } ) ;
//var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o cp-app-drive-icon-expcol"});
var $expandIcon = $ ( '<span>' , { "class" : "fa fa-plus-square-o cp-app-drive-icon-expcol" } ) ;
var $listIcon = $ ( '<button>' , { "class" : "fa fa-list" } ) ;
var $gridIcon = $ ( '<button>' , { "class" : "fa fa-th-large" } ) ;
var $sortAscIcon = $ ( '<span>' , { "class" : "fa fa-angle-up sortasc" } ) ;
var $sortDescIcon = $ ( '<span>' , { "class" : "fa fa-angle-down sortdesc" } ) ;
var $closeIcon = $ ( '<span>' , { "class" : "fa fa-window-close" } ) ;
//var $backupIcon = $('<span>', {"class": "fa fa-life-ring"});
var $searchIcon = $ ( '<span>' , { "class" : "fa fa-search cp-app-drive-tree-search-icon" } ) ;
var $addIcon = $ ( '<span>' , { "class" : "fa fa-plus" } ) ;
var $renamedIcon = $ ( '<span>' , { "class" : "fa fa-flag" } ) ;
var $readonlyIcon = $ ( '<span>' , { "class" : "fa " + faReadOnly } ) ;
var $ownedIcon = $ ( '<span>' , { "class" : "fa fa-id-badge" } ) ;
var $sharedIcon = $ ( '<span>' , { "class" : "fa " + faShared } ) ;
//var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
var $tagsIcon = $ ( '<span>' , { "class" : "fa " + faTags } ) ;
var $passwordIcon = $ ( '<span>' , { "class" : "fa fa-lock" } ) ;
var $restrictedIcon = $ ( '<span>' , { "class" : "fa fa-ban" } ) ;
var $expirableIcon = $ ( '<span>' , { "class" : "fa fa-clock-o" } ) ;
var $separator = $ ( '<div>' , { "class" : "dropdown-divider" } ) ;
var LS _VIEWMODE = "app-drive-viewMode" ;
var FOLDER _CONTENT _ID = "cp-app-drive-content-folder" ;
var config = { } ;
var DEBUG = config . DEBUG = false ;
var debug = config . debug = DEBUG ? function ( ) {
console . log . apply ( console , arguments ) ;
} : function ( ) { return ; } ;
var logError = config . logError = function ( ) {
console . error . apply ( console , arguments ) ;
} ;
var log = config . log = UI . log ;
var localStore = window . cryptpadStore ;
APP . store = { } ;
$ ( window ) . keydown ( function ( e ) {
if ( e . which === 70 && e . ctrlKey ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
if ( APP . displayDirectory ) {
APP . displayDirectory ( [ SEARCH ] ) ;
}
return ;
}
} ) ;
var makeLS = function ( teamId ) {
var suffix = teamId ? ( '-' + teamId ) : '' ;
var LS _LAST = "app-drive-lastOpened" + suffix ;
var LS _OPENED = "app-drive-openedFolders" + suffix ;
var LS = { } ;
LS . getLastOpenedFolder = function ( ) {
var path ;
try {
path = APP . store [ LS _LAST ] ? JSON . parse ( APP . store [ LS _LAST ] ) : [ ROOT ] ;
} catch ( e ) {
path = [ ROOT ] ;
}
return path ;
} ;
LS . setLastOpenedFolder = function ( path ) {
if ( path [ 0 ] === SEARCH ) { return ; }
APP . store [ LS _LAST ] = JSON . stringify ( path ) ;
localStore . put ( LS _LAST , JSON . stringify ( path ) ) ;
} ;
LS . wasFolderOpened = function ( path ) {
var stored = JSON . parse ( APP . store [ LS _OPENED ] || '[]' ) ;
return stored . indexOf ( JSON . stringify ( path ) ) !== - 1 ;
} ;
LS . setFolderOpened = function ( path , opened ) {
var s = JSON . stringify ( path ) ;
var stored = JSON . parse ( APP . store [ LS _OPENED ] || '[]' ) ;
if ( opened && stored . indexOf ( s ) === - 1 ) {
stored . push ( s ) ;
}
if ( ! opened ) {
var idx = stored . indexOf ( s ) ;
if ( idx !== - 1 ) {
stored . splice ( idx , 1 ) ;
}
}
APP . store [ LS _OPENED ] = JSON . stringify ( stored ) ;
localStore . put ( LS _OPENED , JSON . stringify ( stored ) ) ;
} ;
LS . removeFoldersOpened = function ( parentPath ) {
var stored = JSON . parse ( APP . store [ LS _OPENED ] || '[]' ) ;
var s = JSON . stringify ( parentPath ) . slice ( 0 , - 1 ) ;
for ( var i = stored . length - 1 ; i >= 0 ; i -- ) {
if ( stored [ i ] . indexOf ( s ) === 0 ) {
stored . splice ( i , 1 ) ;
}
}
APP . store [ LS _OPENED ] = JSON . stringify ( stored ) ;
localStore . put ( LS _OPENED , JSON . stringify ( stored ) ) ;
} ;
LS . renameFoldersOpened = function ( parentPath , newName ) {
var stored = JSON . parse ( APP . store [ LS _OPENED ] || '[]' ) ;
var s = JSON . stringify ( parentPath ) . slice ( 0 , - 1 ) ;
var newParentPath = parentPath . slice ( ) ;
newParentPath [ newParentPath . length - 1 ] = newName ;
var sNew = JSON . stringify ( newParentPath ) . slice ( 0 , - 1 ) ;
for ( var i = 0 ; i < stored . length ; i ++ ) {
if ( stored [ i ] . indexOf ( s ) === 0 ) {
stored [ i ] = stored [ i ] . replace ( s , sNew ) ;
}
}
APP . store [ LS _OPENED ] = JSON . stringify ( stored ) ;
localStore . put ( LS _OPENED , JSON . stringify ( stored ) ) ;
} ;
LS . moveFoldersOpened = function ( previousPath , newPath ) {
var stored = JSON . parse ( APP . store [ LS _OPENED ] || '[]' ) ;
var s = JSON . stringify ( previousPath ) . slice ( 0 , - 1 ) ;
var sNew = JSON . stringify ( newPath ) . slice ( 0 , - 1 ) ;
if ( s === sNew ) { return ; } // move to itself
if ( sNew . indexOf ( s ) === 0 ) { return ; } // move to subfolder
sNew = JSON . stringify ( newPath . concat ( previousPath [ previousPath . length - 1 ] ) ) . slice ( 0 , - 1 ) ;
for ( var i = 0 ; i < stored . length ; i ++ ) {
if ( stored [ i ] . indexOf ( s ) === 0 ) {
stored [ i ] = stored [ i ] . replace ( s , sNew ) ;
}
}
APP . store [ LS _OPENED ] = JSON . stringify ( stored ) ;
localStore . put ( LS _OPENED , JSON . stringify ( stored ) ) ;
} ;
return LS ;
} ;
var getViewModeClass = function ( forceList ) {
var mode = APP . store [ LS _VIEWMODE ] ;
if ( mode === 'list' || forceList ) { return 'cp-app-drive-content-list' ; }
return 'cp-app-drive-content-grid' ;
} ;
var getViewMode = function ( ) {
return APP . store [ LS _VIEWMODE ] || 'grid' ;
} ;
var setViewMode = function ( mode ) {
if ( typeof ( mode ) !== "string" ) {
logError ( "Incorrect view mode: " , mode ) ;
return ;
}
APP . store [ LS _VIEWMODE ] = mode ;
localStore . put ( LS _VIEWMODE , mode ) ;
} ;
// Handle disconnect/reconnect
// If isHistory and isSf are both false, update the "APP.online" flag
// If isHistory is true, update the "APP.history" flag
// isSf is used to detect offline shared folders: setEditable is called on displayDirectory
var setEditable = function ( state , isHistory , isSf ) {
if ( APP . closed || ! APP . $content || ! $ . contains ( document . documentElement , APP . $content [ 0 ] ) ) { return ; }
if ( isHistory ) {
APP . history = ! state ;
} else if ( ! isSf ) {
APP . online = state ;
}
state = APP . online && ! APP . history && state ;
APP . editable = ! APP . readOnly && state ;
if ( ! state ) {
APP . $content . addClass ( 'cp-app-drive-readonly' ) ;
if ( ! APP . history || ! APP . online ) {
$ ( '#cp-app-drive-connection-state' ) . show ( ) ;
} else {
$ ( '#cp-app-drive-connection-state' ) . hide ( ) ;
}
$ ( '[draggable="true"]' ) . attr ( 'draggable' , false ) ;
}
else {
APP . $content . removeClass ( 'cp-app-drive-readonly' ) ;
$ ( '#cp-app-drive-connection-state' ) . hide ( ) ;
$ ( '[draggable="false"]' ) . attr ( 'draggable' , true ) ;
}
} ;
var copyObjectValue = function ( objRef , objToCopy ) {
for ( var k in objRef ) { delete objRef [ k ] ; }
$ . extend ( true , objRef , objToCopy ) ;
} ;
APP . selectedFiles = [ ] ;
var isElementSelected = function ( $element ) {
var elementId = $element . data ( "path" ) . slice ( - 1 ) [ 0 ] ;
return APP . selectedFiles . indexOf ( elementId ) !== - 1 ;
} ;
var selectElement = function ( $element ) {
var elementId = $element . data ( "path" ) . slice ( - 1 ) [ 0 ] ;
if ( APP . selectedFiles . indexOf ( elementId ) === - 1 ) {
APP . selectedFiles . push ( elementId ) ;
}
$element . addClass ( "cp-app-drive-element-selected" ) ;
} ;
var unselectElement = function ( $element ) {
var elementId = $element . data ( "path" ) . slice ( - 1 ) [ 0 ] ;
var index = APP . selectedFiles . indexOf ( elementId ) ;
if ( index !== - 1 ) {
APP . selectedFiles . splice ( index , 1 ) ;
}
$element . removeClass ( "cp-app-drive-element-selected" ) ;
} ;
var findSelectedElements = function ( ) {
return $ ( ".cp-app-drive-element-selected" ) ;
} ;
var createContextMenu = function ( ) {
var menu = h ( 'div.cp-contextmenu.dropdown.cp-unselectable' , [
h ( 'ul.dropdown-menu' , {
'role' : 'menu' ,
'aria-labelledby' : 'dropdownMenu' ,
'style' : 'display:block;position:static;margin-bottom:5px;'
} , [
h ( 'span.cp-app-drive-context-noAction.dropdown-item.disabled' , Messages . fc _noAction || "No action possible" ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-preview.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : faPreview ,
} , Messages . pad _mediatagPreview ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-open.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : faFolderOpen ,
} , Messages . fc _open ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-openro.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : faReadOnly ,
} , Messages . fc _open _ro ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-openincode.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : faOpenInCode ,
} , Messages . fc _openInCode ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-savelocal.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : 'fa-cloud-upload' ,
} , Messages . pad _mediatagImport ) ) , // Save in your CryptDrive
$separator . clone ( ) [ 0 ] ,
h ( 'li' , h ( 'a.cp-app-drive-context-expandall.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : faExpandAll ,
} , Messages . fc _expandAll ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-collapseall.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : faCollapseAll ,
} , Messages . fc _collapseAll ) ) ,
$separator . clone ( ) [ 0 ] ,
h ( 'li' , h ( 'a.cp-app-drive-context-openparent.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : faShowParent ,
} , Messages . fm _openParent ) ) ,
$separator . clone ( ) [ 0 ] ,
h ( 'li' , h ( 'a.cp-app-drive-context-share.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : 'fa-shhare-alt' ,
} , Messages . shareButton ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-access.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : faAccess ,
} , Messages . accessButton ) ) ,
$separator . clone ( ) [ 0 ] ,
h ( 'li' , h ( 'a.cp-app-drive-context-newfolder.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faFolder ,
} , Messages . fc _newfolder ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-newsharedfolder.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faSharedFolder ,
} , Messages . fc _newsharedfolder ) ) ,
$separator . clone ( ) [ 0 ] ,
h ( 'li' , h ( 'a.cp-app-drive-context-uploadfiles.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faUploadFiles ,
} , Messages . uploadButton ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-uploadfolder.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faUploadFolder ,
} , Messages . uploadFolderButton ) ) ,
$separator . clone ( ) [ 0 ] ,
h ( 'li' , h ( 'a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : AppConfig . applicationsIcon . pad ,
'data-type' : 'pad'
} , Messages . button _newpad ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : AppConfig . applicationsIcon . code ,
'data-type' : 'code'
} , Messages . button _newcode ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : AppConfig . applicationsIcon . slide ,
'data-type' : 'slide'
} , Messages . button _newslide ) ) ,
h ( 'li.dropdown-submenu' , [
h ( 'a.cp-app-drive-context-newdocmenu.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : "fa-plus" ,
} , Messages . fm _morePads ) ,
h ( "ul.dropdown-menu" , [
h ( 'li' , h ( 'a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : AppConfig . applicationsIcon . sheet ,
'data-type' : 'sheet'
} , Messages . button _newsheet ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : AppConfig . applicationsIcon . whiteboard ,
'data-type' : 'whiteboard'
} , Messages . button _newwhiteboard ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : AppConfig . applicationsIcon . kanban ,
'data-type' : 'kanban'
} , Messages . button _newkanban ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : AppConfig . applicationsIcon . poll ,
'data-type' : 'poll'
} , Messages . button _newpoll ) ) ,
] ) ,
] ) ,
$separator . clone ( ) [ 0 ] ,
h ( 'li' , h ( 'a.cp-app-drive-context-empty.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faEmpty ,
} , Messages . fc _empty ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-restore.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faRestore ,
} , Messages . fc _restore ) ) ,
$separator . clone ( ) [ 0 ] ,
h ( 'li' , h ( 'a.cp-app-drive-context-rename.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faRename ,
} , Messages . fc _rename ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-color.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faColor ,
} , Messages . fc _color ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-hashtag.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faTags ,
} , Messages . fc _hashtag ) ) ,
$separator . clone ( ) [ 0 ] ,
h ( 'li' , h ( 'a.cp-app-drive-context-makeacopy.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faCopy ,
} , Messages . makeACopy ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-download.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : faDownload ,
} , Messages . download _mt _button ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-delete.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faTrash ,
} , Messages . fc _delete ) ) , // "Move to trash"
h ( 'li' , h ( 'a.cp-app-drive-context-deleteowned.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faDelete ,
} , Messages . fc _delete _owned ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-remove.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faTrash ,
} , Messages . fc _remove ) ) ,
h ( 'li' , h ( 'a.cp-app-drive-context-removesf.dropdown-item.cp-app-drive-context-editable' , {
'tabindex' : '-1' ,
'data-icon' : faTrash ,
} , Messages . fc _remove _sharedfolder ) ) ,
$separator . clone ( ) [ 0 ] ,
h ( 'li' , h ( 'a.cp-app-drive-context-properties.dropdown-item' , {
'tabindex' : '-1' ,
'data-icon' : faProperties ,
} , Messages . fc _prop ) )
] )
] ) ;
// add icons to the contextmenu options
$ ( menu ) . find ( "li a.dropdown-item" ) . each ( function ( i , el ) {
var $icon = $ ( "<span>" ) ;
if ( $ ( el ) . attr ( 'data-icon' ) ) {
var font = $ ( el ) . attr ( 'data-icon' ) . indexOf ( 'cptools' ) === 0 ? 'cptools' : 'fa' ;
$icon . addClass ( font ) . addClass ( $ ( el ) . attr ( 'data-icon' ) ) ;
} else {
$icon . text ( $ ( el ) . text ( ) ) ;
}
$ ( el ) . prepend ( $icon ) ;
} ) ;
// add events handlers for the contextmenu submenus
$ ( menu ) . find ( ".dropdown-submenu" ) . each ( function ( i , el ) {
var $el = $ ( el ) ;
var $a = $el . children ( ) . filter ( "a" ) ;
var $sub = $el . find ( ".dropdown-menu" ) . first ( ) ;
var left , bottomOffset ;
var timeoutId ;
var showSubmenu = function ( ) {
clearTimeout ( timeoutId ) ;
left = $el . offset ( ) . left + $el . outerWidth ( ) + $sub . outerWidth ( ) > $ ( window ) . width ( ) ;
bottomOffset = $el . offset ( ) . top + $el . outerHeight ( ) + $sub . outerHeight ( ) - $ ( window ) . height ( ) ;
$sub . css ( "left" , left ? "0%" : "100%" ) ;
$sub . css ( "transform" , "translate("
+ ( left ? "-100%" : "0" )
+ "," + ( bottomOffset > 0 ? - ( bottomOffset - $el . outerHeight ( ) ) : 0 ) + "px)" ) ;
$el . siblings ( ) . find ( ".dropdown-menu" ) . hide ( ) ;
$sub . show ( ) ;
} ;
var hideSubmenu = function ( ) {
$sub . hide ( ) ;
$sub . removeClass ( "left" ) ;
} ;
var mouseOutSubmenu = function ( ) {
// don't hide immediately the submenu
timeoutId = setTimeout ( hideSubmenu , 100 ) ;
} ;
// Add submenu expand icon
$a . append ( h ( "span.dropdown-toggle" ) ) ;
// Show / hide submenu
$el . hover ( function ( ) {
showSubmenu ( ) ;
} , function ( ) {
mouseOutSubmenu ( ) ;
} ) ;
// handle click event
$el . click ( function ( e ) {
var targetItem = $ ( e . target ) . closest ( ".dropdown-item" ) [ 0 ] ; // don't close contextmenu if open submenu
var elTarget = $el . children ( ".dropdown-item" ) [ 0 ] ;
if ( targetItem === elTarget ) { e . stopPropagation ( ) ; }
if ( $el . children ( ) . filter ( ".dropdown-menu:visible" ) . length !== 0 ) {
$el . find ( ".dropdown-menu" ) . hide ( ) ;
hideSubmenu ( ) ;
}
else {
showSubmenu ( ) ;
}
} ) ;
} ) ;
return $ ( menu ) ;
} ;
var create = function ( common , driveConfig ) { //proxy, folders) {
var metadataMgr = common . getMetadataMgr ( ) ;
var sframeChan = common . getSframeChannel ( ) ;
var priv = metadataMgr . getPrivateData ( ) ;
// Initialization
Util . extend ( APP , driveConfig . APP ) ;
APP . $limit = driveConfig . $limit ;
var proxy = driveConfig . proxy ;
var folders = driveConfig . folders ;
var files = proxy . drive ;
var history = driveConfig . history || { } ;
var edPublic = driveConfig . edPublic || priv . edPublic ;
config . editKey = driveConfig . editKey ;
APP . origin = priv . origin ;
APP . hideDuplicateOwned = Util . find ( priv , [ 'settings' , 'drive' , 'hideDuplicate' ] ) ;
APP . closed = false ;
APP . toolbar = driveConfig . toolbar ;
var $readOnly = $ ( h ( 'div#cp-app-drive-edition-state.cp-app-drive-content-info-box' , Messages . readonly ) ) ;
var updateObject = driveConfig . updateObject ;
var updateSharedFolders = driveConfig . updateSharedFolders ;
// manager
config . loggedIn = APP . loggedIn ;
config . sframeChan = sframeChan ;
var manager = ProxyManager . createInner ( files , sframeChan , edPublic , config ) ;
var LS = makeLS ( APP . team ) ;
Object . keys ( folders ) . forEach ( function ( id ) {
var f = folders [ id ] ;
var sfData = files . sharedFolders [ id ] || { } ;
var href = manager . user . userObject . getHref ( sfData ) ;
var parsed = Hash . parsePadUrl ( href ) ;
var secret = Hash . getSecrets ( 'drive' , parsed . hash , sfData . password ) ;
manager . addProxy ( id , { proxy : f } , null , secret . keys . secondaryKey ) ;
} ) ;
// UI containers
var $tree = APP . $tree = $ ( "#cp-app-drive-tree" ) ;
var $content = APP . $content = $ ( "#cp-app-drive-content" ) ;
var $appContainer = $ ( ".cp-app-drive-container" ) ;
var $driveToolbar = APP . toolbar . $bottom ;
var $contextMenu = createContextMenu ( ) . appendTo ( $appContainer ) ;
var $contentContextMenu = $ ( "#cp-app-drive-context-content" ) ;
var $defaultContextMenu = $ ( "#cp-app-drive-context-default" ) ;
var $trashTreeContextMenu = $ ( "#cp-app-drive-context-trashtree" ) ;
var $trashContextMenu = $ ( "#cp-app-drive-context-trash" ) ;
// TOOLBAR
// DRIVE
var currentPath = APP . currentPath = LS . getLastOpenedFolder ( ) ;
if ( APP . newSharedFolder ) {
var newSFPaths = manager . findFile ( APP . newSharedFolder ) ;
if ( newSFPaths . length ) {
currentPath = newSFPaths [ 0 ] ;
}
}
// Categories dislayed in the menu
var displayedCategories = [ ROOT , TRASH , SEARCH , RECENT ] ;
// PCS enabled: display owned pads
//if (AppConfig.displayCreationScreen) { displayedCategories.push(OWNED); }
// Templates enabled: display template category
if ( AppConfig . enableTemplates ) { displayedCategories . push ( TEMPLATE ) ; }
// Tags used: display Tags category
if ( Object . keys ( manager . getTagsList ( ) ) . length ) { displayedCategories . push ( TAGS ) ; }
var virtualCategories = [ SEARCH , RECENT , OWNED , TAGS ] ;
if ( ! APP . loggedIn ) {
$tree . hide ( ) ;
if ( APP . newSharedFolder ) {
// ANON_SHARED_FOLDER
displayedCategories = [ SHARED _FOLDER ] ;
virtualCategories . push ( SHARED _FOLDER ) ;
currentPath = [ SHARED _FOLDER , ROOT ] ;
} else {
displayedCategories = [ FILES _DATA ] ;
currentPath = [ FILES _DATA ] ;
if ( Object . keys ( files . root ) . length && ! proxy . anonymousAlert ) {
var msg = common . fixLinks ( $ ( '<div>' ) . html ( Messages . fm _alert _anonymous ) ) ;
UI . alert ( msg ) ;
proxy . anonymousAlert = true ;
}
}
}
APP . editable = ! APP . readOnly ;
var appStatus = {
isReady : true ,
_onReady : [ ] ,
onReady : function ( handler ) {
if ( appStatus . isReady ) {
handler ( ) ;
return ;
}
appStatus . _onReady . push ( handler ) ;
} ,
ready : function ( state ) {
appStatus . isReady = state ;
if ( state ) {
appStatus . _onReady . forEach ( function ( h ) {
h ( ) ;
} ) ;
appStatus . _onReady = [ ] ;
}
}
} ;
var findDataHolder = function ( $el ) {
return $el . is ( '.cp-app-drive-element-row' ) ? $el : $el . closest ( '.cp-app-drive-element-row' ) ;
} ;
// Selection
var sel = { } ;
var removeSelected = function ( keepObj ) {
APP . selectedFiles = [ ] ;
findSelectedElements ( ) . removeClass ( "cp-app-drive-element-selected" ) ;
var $container = $driveToolbar . find ( '#cp-app-drive-toolbar-contextbuttons' ) ;
if ( ! $container . length ) { return ; }
$container . html ( '' ) ;
if ( ! keepObj ) {
delete sel . startSelected ;
delete sel . endSelected ;
delete sel . oldSelection ;
}
} ;
sel . refresh = 50 ;
sel . $selectBox = $ ( '<div>' , { 'class' : 'cp-app-drive-content-select-box' } ) . appendTo ( $content ) ;
var checkSelected = function ( ) {
if ( ! sel . down ) { return ; }
var pos = sel . pos ;
var l = $content [ 0 ] . querySelectorAll ( '.cp-app-drive-element:not(.cp-app-drive-element-selected):not(.cp-app-drive-element-header)' ) ;
var p , el ;
var offset = getViewMode ( ) === "grid" ? 10 : 0 ;
for ( var i = 0 ; i < l . length ; i ++ ) {
el = l [ i ] ;
p = $ ( el ) . position ( ) ;
p . top += offset + $content . scrollTop ( ) ;
p . left += offset ;
p . bottom = p . top + $ ( el ) . outerHeight ( ) ;
p . right = p . left + $ ( el ) . outerWidth ( ) ;
if ( p . right < pos . left || p . left > pos . right
|| p . top > pos . bottom || p . bottom < pos . top ) {
$ ( el ) . removeClass ( 'cp-app-drive-element-selected-tmp' ) ;
} else {
$ ( el ) . addClass ( 'cp-app-drive-element-selected-tmp' ) ;
}
}
} ;
$content . on ( 'mousedown' , function ( e ) {
if ( e . which !== 1 ) { return ; }
$content . focus ( ) ;
sel . down = true ;
if ( ! e . ctrlKey ) { removeSelected ( ) ; }
var rect = e . currentTarget . getBoundingClientRect ( ) ;
sel . startX = e . clientX - rect . left ;
sel . startY = e . clientY - rect . top + $content . scrollTop ( ) ;
sel . $selectBox . show ( ) . css ( {
left : sel . startX + 'px' ,
top : sel . startY + 'px' ,
width : '0px' ,
height : '0px'
} ) ;
APP . hideMenu ( e ) ;
if ( sel . move ) { return ; }
sel . move = function ( ev ) {
var rectMove = ev . currentTarget . getBoundingClientRect ( ) ,
offX = ev . clientX - rectMove . left ,
offY = ev . clientY - rectMove . top + $content . scrollTop ( ) ;
var left = sel . startX ,
top = sel . startY ;
var width = offX - sel . startX ;
if ( width < 0 ) {
left = Math . max ( 0 , offX ) ;
var diffX = left - offX ;
width = Math . abs ( width ) - diffX ;
}
var height = offY - sel . startY ;
if ( height < 0 ) {
top = Math . max ( 0 , offY ) ;
var diffY = top - offY ;
height = Math . abs ( height ) - diffY ;
}
sel . $selectBox . css ( {
width : width + 'px' ,
left : left + 'px' ,
height : height + 'px' ,
top : top + 'px'
} ) ;
sel . pos = {
top : top ,
left : left ,
bottom : top + height ,
right : left + width
} ;
var diffT = sel . update ? + new Date ( ) - sel . update : sel . refresh ;
if ( diffT < sel . refresh ) {
if ( ! sel . to ) {
sel . to = window . setTimeout ( function ( ) {
sel . update = + new Date ( ) ;
checkSelected ( ) ;
sel . to = undefined ;
} , ( sel . refresh - diffT ) ) ;
}
return ;
}
sel . update = + new Date ( ) ;
checkSelected ( ) ;
} ;
$content . mousemove ( sel . move ) ;
} ) ;
var onWindowMouseUp = function ( e ) {
if ( ! sel . down ) { return ; }
if ( e . which !== 1 ) { return ; }
sel . down = false ;
sel . $selectBox . hide ( ) ;
$content . off ( 'mousemove' , sel . move ) ;
delete sel . move ;
$content . find ( '.cp-app-drive-element-selected-tmp' )
. removeClass ( 'cp-app-drive-element-selected-tmp' )
. each ( function ( idx , element ) {
selectElement ( $ ( element ) ) ;
} ) ;
e . stopPropagation ( ) ;
} ;
var getSelectedPaths = function ( $element ) {
var paths = [ ] ;
if ( ! $element || $element . length === 0 ) { return paths ; }
if ( findSelectedElements ( ) . length > 1 ) {
var $selected = findSelectedElements ( ) ;
$selected . each ( function ( idx , elmt ) {
var ePath = $ ( elmt ) . data ( 'path' ) ;
if ( ePath ) {
paths . push ( {
path : ePath ,
element : $ ( elmt )
} ) ;
}
} ) ;
}
if ( ! paths . length ) {
var path = $element . data ( 'path' ) ;
if ( ! path ) { return false ; }
paths . push ( {
path : path ,
element : $element
} ) ;
}
return paths ;
} ;
var removeInput = function ( cancel ) {
if ( ! cancel && $ ( '.cp-app-drive-element-row > input' ) . length === 1 ) {
var $input = $ ( '.cp-app-drive-element-row > input' ) ;
manager . rename ( $input . data ( 'path' ) , $input . val ( ) , APP . refresh ) ;
}
$ ( '.cp-app-drive-element-row > input' ) . remove ( ) ;
$ ( '.cp-app-drive-element-row > span:hidden' ) . removeAttr ( 'style' ) ;
} ;
var getFileNameExtension = function ( name ) {
var matched = /\.[^\. ]+$/ . exec ( name ) ;
if ( matched && matched . length ) { return matched [ matched . length - 1 ] ; }
return '' ;
} ;
// Replace a file/folder name by an input to change its value
var displayRenameInput = function ( $element , path ) {
// NOTE: setTimeout(f, 0) otherwise the "rename" button in the toolbar is not working
window . setTimeout ( function ( ) {
if ( ! APP . editable ) { return ; }
if ( ! path || path . length < 2 ) {
logError ( "Renaming a top level element (root, trash or filesData) is forbidden." ) ;
return ;
}
removeInput ( ) ;
var $name = $element . find ( '.cp-app-drive-element-name' ) ;
if ( ! $name . length ) {
$name = $element . find ( '> .cp-app-drive-element' ) ;
}
$name . hide ( ) ;
var isFolder = $element . is ( ".cp-app-drive-element-folder:not(.cp-app-drive-element-sharedf)" ) ;
var el = manager . find ( path ) ;
var name = manager . isFile ( el ) ? manager . getTitle ( el ) : path [ path . length - 1 ] ;
if ( manager . isSharedFolder ( el ) ) {
name = manager . getSharedFolderData ( el ) . title ;
}
var $input = $ ( '<input>' , {
placeholder : name ,
value : name
} ) . data ( 'path' , path ) ;
// Stop propagation on keydown to avoid issues with arrow keys
$input . on ( 'keydown' , function ( e ) { e . stopPropagation ( ) ; } ) ;
$input . on ( 'keyup' , function ( e ) {
e . stopPropagation ( ) ;
if ( e . which === 13 ) {
removeInput ( true ) ;
var newName = $input . val ( ) ;
if ( JSON . stringify ( path ) === JSON . stringify ( currentPath ) ) {
manager . rename ( path , $input . val ( ) , function ( ) {
if ( isFolder ) {
LS . renameFoldersOpened ( path , newName ) ;
path [ path . length - 1 ] = newName ;
}
APP . displayDirectory ( path ) ;
} ) ;
}
else {
manager . rename ( path , $input . val ( ) , function ( ) {
if ( isFolder ) {
LS . renameFoldersOpened ( path , newName ) ;
unselectElement ( $element ) ;
$element . data ( "path" , $element . data ( "path" ) . slice ( 0 , - 1 ) . concat ( newName ) ) ;
selectElement ( $element ) ;
}
APP . refresh ( ) ;
} ) ;
}
return ;
}
if ( e . which === 27 ) {
removeInput ( true ) ;
}
} ) . on ( 'keypress' , function ( e ) { e . stopPropagation ( ) ; } ) ;
//$element.parent().append($input);
$name . after ( $input ) ;
$input . focus ( ) ;
var extension = getFileNameExtension ( name ) ;
var input = $input [ 0 ] ;
input . selectionStart = 0 ;
input . selectionEnd = name . length - extension . length ;
// We don't want to open the file/folder when clicking on the input
$input . on ( 'click dblclick' , function ( e ) {
e . stopPropagation ( ) ;
} ) ;
// Remove the browser ability to drag text from the input to avoid
// triggering our drag/drop event handlers
$input . on ( 'dragstart dragleave drag drop' , function ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
} ) ;
// Make the parent element non-draggable when selecting text in the field
// since it would remove the input
$input . on ( 'mousedown' , function ( e ) {
e . stopPropagation ( ) ;
$input . parents ( '.cp-app-drive-element-row' ) . attr ( "draggable" , false ) ;
} ) ;
$input . on ( 'mouseup' , function ( e ) {
e . stopPropagation ( ) ;
$input . parents ( '.cp-app-drive-element-row' ) . attr ( "draggable" , true ) ;
} ) ;
} , 0 ) ;
} ;
// Arrow keys to modify the selection
var onWindowKeydown = function ( e ) {
if ( ! $content . is ( ':visible' ) ) { return ; }
var $searchBar = $tree . find ( '#cp-app-drive-tree-search-input' ) ;
if ( document . activeElement && document . activeElement . nodeName === 'INPUT' ) { return ; }
if ( $searchBar . is ( ':focus' ) && $searchBar . val ( ) ) { return ; }
var $elements = $content . find ( '.cp-app-drive-element:not(.cp-app-drive-element-header)' ) ;
var ev = { } ;
if ( e . ctrlKey ) { ev . ctrlKey = true ; }
if ( e . shiftKey ) { ev . shiftKey = true ; }
// ESC
if ( e . which === 27 ) {
return void APP . hideMenu ( ) ;
}
// Enter
if ( e . which === 13 ) {
var $allSelected = $content . find ( '.cp-app-drive-element.cp-app-drive-element-selected' ) ;
if ( $allSelected . length === 1 ) {
// Open the folder or the file
$allSelected . dblclick ( ) ;
return ;
}
// If more than one, open only the files
var $select = $content . find ( '.cp-app-drive-element-file.cp-app-drive-element-selected' ) ;
$select . each ( function ( idx , el ) {
$ ( el ) . dblclick ( ) ;
} ) ;
return ;
}
// Ctrl+A select all
if ( e . which === 65 && ( e . ctrlKey || ( e . metaKey && APP . isMac ) ) ) {
e . preventDefault ( ) ;
$content . find ( '.cp-app-drive-element:not(.cp-app-drive-element-selected)' )
. each ( function ( idx , element ) {
selectElement ( $ ( element ) ) ;
} ) ;
return ;
}
// F2: rename selected element
if ( e . which === 113 ) {
var paths = getSelectedPaths ( findSelectedElements ( ) . first ( ) ) ;
if ( paths . length !== 1 ) { return ; }
displayRenameInput ( paths [ 0 ] . element , paths [ 0 ] . path ) ;
}
// [Left, Up, Right, Down]
if ( [ 37 , 38 , 39 , 40 ] . indexOf ( e . which ) === - 1 ) { return ; }
e . preventDefault ( ) ;
var click = function ( el ) {
if ( ! el ) { return ; }
APP . onElementClick ( ev , $ ( el ) ) ;
} ;
var $selection = findSelectedElements ( ) ;
if ( $selection . length === 0 ) { return void click ( $elements . first ( ) [ 0 ] ) ; }
var lastIndex = typeof sel . endSelected === "number" ? sel . endSelected :
typeof sel . startSelected === "number" ? sel . startSelected :
$elements . index ( $selection . last ( ) [ 0 ] ) ;
var length = $elements . length ;
if ( length === 0 ) { return ; }
// List mode
if ( getViewMode ( ) === "list" ) {
if ( e . which === 40 ) { click ( $elements . get ( Math . min ( lastIndex + 1 , length - 1 ) ) ) ; }
if ( e . which === 38 ) { click ( $elements . get ( Math . max ( lastIndex - 1 , 0 ) ) ) ; }
return ;
}
// Icon mode
// Get the vertical and horizontal position of lastIndex
// Filter all the elements to get those in the same line/column
var pos = $ ( $elements . get ( 0 ) ) . position ( ) ;
var $line = $elements . filter ( function ( idx , el ) {
return $ ( el ) . position ( ) . top === pos . top ;
} ) ;
var cols = $line . length ;
var lines = Math . ceil ( length / cols ) ;
var lastPos = {
l : Math . floor ( lastIndex / cols ) ,
c : lastIndex - Math . floor ( lastIndex / cols ) * cols
} ;
if ( e . which === 37 ) {
if ( lastPos . c === 0 ) { return ; }
click ( $elements . get ( Math . max ( lastIndex - 1 , 0 ) ) ) ;
return ;
}
if ( e . which === 38 ) {
if ( lastPos . l === 0 ) { return ; }
click ( $elements . get ( Math . max ( lastIndex - cols , 0 ) ) ) ;
return ;
}
if ( e . which === 39 ) {
if ( lastPos . c === cols - 1 ) { return ; }
click ( $elements . get ( Math . min ( lastIndex + 1 , length - 1 ) ) ) ;
return ;
}
if ( e . which === 40 ) {
if ( lastPos . l === lines - 1 ) { return ; }
click ( $elements . get ( Math . min ( lastIndex + cols , length - 1 ) ) ) ;
return ;
}
} ;
var compareDays = function ( date1 , date2 ) {
var day1 = Date . UTC ( date1 . getFullYear ( ) , date1 . getMonth ( ) , date1 . getDate ( ) ) ;
var day2 = Date . UTC ( date2 . getFullYear ( ) , date2 . getMonth ( ) , date2 . getDate ( ) ) ;
var ms = Math . abs ( day1 - day2 ) ;
return Math . floor ( ms / 1000 / 60 / 60 / 24 ) ;
} ;
var getDate = function ( sDate ) {
if ( ! sDate ) { return '' ; }
var ret = sDate . toString ( ) ;
try {
var date = new Date ( sDate ) ;
var today = new Date ( ) ;
var diff = compareDays ( date , today ) ;
if ( diff === 0 ) {
ret = date . toLocaleTimeString ( ) ;
} else {
ret = date . toLocaleDateString ( ) ;
}
} catch ( e ) {
console . error ( "Unable to format that string to a date with .toLocaleString" , sDate , e ) ;
}
return ret ;
} ;
var previewMediaTag = function ( data ) {
var mts = [ ] ;
$content . find ( '.cp-app-drive-element.cp-border-color-file' ) . each ( function ( i , el ) {
var path = $ ( el ) . data ( 'path' ) ;
var id = manager . find ( path ) ;
if ( ! id ) { return ; }
var _data = manager . getFileData ( id ) ;
if ( ! _data || _data . channel < 48 ) { return ; }
mts . push ( {
channel : _data . channel ,
href : _data . href ,
password : _data . password
} ) ;
} ) ;
// Find initial position
var idx = - 1 ;
mts . some ( function ( obj , i ) {
if ( obj . channel === data . channel ) {
idx = i ;
return true ;
}
} ) ;
if ( idx === - 1 ) {
mts . unshift ( {
href : data . href ,
password : data . password
} ) ;
idx = 0 ;
}
common . getMediaTagPreview ( mts , idx ) ;
} ;
// `app`: true (force open wiht the app), false (force open in preview),
// falsy (open in preview if default is not using the app)
var defaultInApp = [ 'application/pdf' ] ;
var openFile = function ( el , isRo , app ) {
var data = manager . getFileData ( el ) ;
if ( ! data || ( ! data . href && ! data . roHref ) ) {
return void logError ( "Missing data for the file" , el , data ) ;
}
var href = isRo ? data . roHref : ( data . href || data . roHref ) ;
var parsed = Hash . parsePadUrl ( href ) ;
if ( parsed . hashData && parsed . hashData . type === 'file' && ! app
&& ( defaultInApp . indexOf ( data . fileType ) === - 1 || app === false ) ) {
return void previewMediaTag ( data ) ;
}
var priv = metadataMgr . getPrivateData ( ) ;
var useUnsafe = Util . find ( priv , [ 'settings' , 'security' , 'unsafeLinks' ] ) ;
if ( useUnsafe === true ) {
return void window . open ( APP . origin + href ) ;
}
// Get hidden hash
var secret = Hash . getSecrets ( parsed . type , parsed . hash , data . password ) ;
var opts = { } ;
if ( isRo ) { opts . view = true ; }
var hash = Hash . getHiddenHashFromKeys ( parsed . type , secret , opts ) ;
var hiddenHref = Hash . hashToHref ( hash , parsed . type ) ;
window . open ( APP . origin + hiddenHref ) ;
} ;
var openIn = function ( type , path , team , fData ) {
var obj = {
p : path ,
t : team ,
d : fData
} ;
var href = Hash . hashToHref ( '' , type ) ;
common . openURL ( Hash . getNewPadURL ( href , obj ) ) ;
} ;
var refresh = APP . refresh = function ( ) {
APP . displayDirectory ( currentPath ) ;
} ;
var pickFolderColor = function ( $element , currentColor , cb ) {
var colors = [ "" , "#f23c38" , "#ff0073" , "#da0eba" , "#9d00ac" , "#6c19b3" , "#4a42b1" , "#3d8af0" , "#30a0f1" , "#1fb9d1" , "#009686" , "#45b354" , "#84c750" , "#c6e144" , "#faf147" , "#fbc423" , "#fc9819" , "#fd5227" , "#775549" , "#9c9c9c" , "#607a89" ] ;
var colorsElements = [ ] ;
var currentElement = null ;
colors . forEach ( function ( color , i ) {
var element = h ( "span.cp-app-drive-color-picker-color" , [
h ( "span.cptools.cp-app-drive-icon-folder.cp-app-drive-content-icon" + ( i === 0 ? ".cptools-folder-no-color" : ".cptools-folder" ) ) ,
h ( "span.fa.fa-check" )
] ) ;
$ ( element ) . css ( "color" , colors [ i ] ) ;
if ( colors [ i ] === currentColor ) {
currentElement = element ;
$ ( element ) . addClass ( "cp-app-drive-current-color" ) ;
}
$ ( element ) . on ( "click" , function ( ) {
$ ( currentElement ) . removeClass ( "cp-app-drive-current-color" ) ;
currentElement = element ;
$ ( element ) . addClass ( "cp-app-drive-current-color" ) ;
cb ( color ) ;
} ) ;
colorsElements . push ( element ) ;
} ) ;
var content = h ( "div.cp-app-drive-color-picker" , colorsElements ) ;
UI . alert ( content ) ;
} ;
var getFolderColor = function ( path ) {
if ( path . length === 0 ) { return ; }
return manager . getFolderData ( path ) . color || "" ;
} ;
var setFolderColor = function ( $element , path , color ) {
if ( $element . length === 0 ) { return ; }
$element . find ( ".cp-app-drive-icon-folder" ) . css ( "color" , color ) ;
manager . setFolderData ( {
path : path ,
key : "color" ,
value : color
} , function ( ) { } ) ;
} ;
var filterContextMenu = function ( type , paths ) {
if ( ! paths || paths . length === 0 ) { logError ( 'no paths' ) ; }
$contextMenu . find ( 'li' ) . hide ( ) ;
var show = [ ] ;
var filter ;
var editable = true ;
if ( type === "content" ) {
if ( APP . $content . data ( 'readOnlyFolder' ) ) { editable = false ; }
// Return true in filter to hide
filter = function ( $el , className ) {
if ( className === 'newfolder' ) { return ; }
if ( className === 'newsharedfolder' ) {
// Hide the new shared folder menu if we're already in a shared folder
return manager . isInSharedFolder ( currentPath ) || APP . disableSF ;
}
if ( className === 'uploadfiles' ) { return ; }
if ( className === 'uploadfolder' ) { return ! APP . allowFolderUpload ; }
if ( className === 'newdoc' ) {
return AppConfig . availablePadTypes . indexOf ( $el . attr ( 'data-type' ) ) === - 1 ;
}
} ;
} else {
// In case of multiple selection, we must hide the option if at least one element
// is not compatible
var containsFolder = false ;
var hide = [ ] ;
if ( ! APP . team ) {
hide . push ( 'savelocal' ) ;
}
paths . forEach ( function ( p ) {
var path = p . path ;
var $element = p . element ;
if ( APP . $content . data ( 'readOnlyFolder' ) &&
manager . isSubpath ( path , currentPath ) ) { editable = false ; }
if ( ! $element . closest ( "#cp-app-drive-tree" ) . length ) {
hide . push ( 'expandall' ) ;
hide . push ( 'collapseall' ) ;
}
if ( path . length === 1 ) {
// Can't rename, share, delete, or change the color of categories
hide . push ( 'delete' ) ;
hide . push ( 'rename' ) ;
hide . push ( 'share' ) ;
hide . push ( 'savelocal' ) ;
hide . push ( 'color' ) ;
}
if ( ! $element . is ( '.cp-app-drive-element-owned' ) ) {
hide . push ( 'deleteowned' ) ;
}
if ( $element . is ( '.cp-app-drive-element-restricted' ) ) {
hide . push ( 'rename' , 'download' , 'share' , 'access' , 'color' ) ;
}
if ( $element . is ( '.cp-app-drive-element-notrash' ) ) {
// We can't delete elements in virtual categories
hide . push ( 'delete' ) ;
}
if ( ! $element . is ( '.cp-border-color-file' ) ) {
//hide.push('download');
hide . push ( 'openincode' ) ;
hide . push ( 'preview' ) ;
}
if ( $element . is ( '.cp-border-color-sheet' ) ) {
hide . push ( 'download' ) ;
}
if ( $element . is ( '.cp-app-drive-element-file' ) ) {
// No folder in files
hide . push ( 'color' ) ;
hide . push ( 'newfolder' ) ;
if ( $element . is ( '.cp-app-drive-element-readonly' ) ) {
hide . push ( 'open' ) ; // Remove open 'edit' mode
} else if ( $element . is ( '.cp-app-drive-element-noreadonly' ) ) {
hide . push ( 'openro' ) ; // Remove open 'view' mode
}
var metadata = manager . getFileData ( manager . find ( path ) ) ;
if ( ! metadata || ! Util . isPlainTextFile ( metadata . fileType , metadata . title ) ) {
hide . push ( 'openincode' ) ;
}
if ( metadata . channel && metadata . channel . length < 48 ) {
hide . push ( 'preview' ) ;
}
if ( ! metadata . channel || metadata . channel . length > 32 || metadata . rtChannel ) {
hide . push ( 'makeacopy' ) ; // Not for blobs
}
} else if ( $element . is ( '.cp-app-drive-element-sharedf' ) ) {
if ( containsFolder ) {
// More than 1 folder selected: cannot create a new subfolder
hide . push ( 'newfolder' ) ;
hide . push ( 'expandall' ) ;
hide . push ( 'collapseall' ) ;
}
containsFolder = true ;
hide . push ( 'openro' ) ;
hide . push ( 'openincode' ) ;
hide . push ( 'hashtag' ) ;
//hide.push('delete');
hide . push ( 'makeacopy' ) ;
//hide.push('deleteowned');
} else { // it's a folder
if ( containsFolder ) {
// More than 1 folder selected: cannot create a new subfolder
hide . push ( 'newfolder' ) ;
hide . push ( 'expandall' ) ;
hide . push ( 'collapseall' ) ;
}
containsFolder = true ;
hide . push ( 'savelocal' ) ;
hide . push ( 'openro' ) ;
hide . push ( 'openincode' ) ;
hide . push ( 'properties' , 'access' ) ;
hide . push ( 'hashtag' ) ;
hide . push ( 'makeacopy' ) ;
}
// If we're in the trash, hide restore and properties for non-root elements
if ( type === "trash" && path && path . length > 4 ) {
hide . push ( 'restore' ) ;
hide . push ( 'properties' ) ;
}
// If we're not in the trash nor in a shared folder, hide "remove"
if ( ! manager . isInSharedFolder ( path )
&& ! $element . is ( '.cp-app-drive-element-sharedf' ) ) {
// This isn't a shared folder: can't delete shared folder
hide . push ( 'removesf' ) ;
} else if ( type === "tree" ) {
// This is a shared folder or an element inside a shsared folder
// ==> can't move to trash
hide . push ( 'delete' ) ;
}
if ( $element . closest ( '[data-ro]' ) . length ) {
editable = false ;
}
} ) ;
if ( paths . length > 1 ) {
hide . push ( 'restore' ) ;
hide . push ( 'properties' , 'access' ) ;
hide . push ( 'rename' ) ;
hide . push ( 'openparent' ) ;
hide . push ( 'download' ) ;
hide . push ( 'share' ) ;
hide . push ( 'savelocal' ) ;
hide . push ( 'openincode' ) ; // can't because of race condition
hide . push ( 'makeacopy' ) ;
hide . push ( 'preview' ) ;
}
if ( containsFolder && paths . length > 1 ) {
// Cannot open multiple folders
hide . push ( 'open' ) ;
}
if ( ! APP . loggedIn ) {
hide . push ( 'openparent' ) ;
hide . push ( 'rename' ) ;
}
filter = function ( $el , className ) {
if ( hide . indexOf ( className ) !== - 1 ) { return true ; }
} ;
}
switch ( type ) {
case 'content' :
show = [ 'newfolder' , 'newsharedfolder' , 'uploadfiles' , 'uploadfolder' , 'newdoc' ] ;
break ;
case 'tree' :
show = [ 'open' , 'openro' , 'preview' , 'openincode' , 'expandall' , 'collapseall' ,
'color' , 'download' , 'share' , 'savelocal' , 'rename' , 'delete' , 'makeacopy' ,
'deleteowned' , 'removesf' , 'access' , 'properties' , 'hashtag' ] ;
break ;
case 'default' :
show = [ 'open' , 'openro' , 'preview' , 'openincode' , 'share' , 'download' , 'openparent' , 'delete' , 'deleteowned' , 'properties' , 'access' , 'hashtag' , 'makeacopy' , 'savelocal' , 'rename' ] ;
break ;
case 'trashtree' : {
show = [ 'empty' ] ;
break ;
}
case 'trash' : {
show = [ 'remove' , 'deleteowned' , 'restore' , 'properties' ] ;
}
}
var filtered = [ ] ;
show . forEach ( function ( className ) {
var $el = $contextMenu . find ( '.cp-app-drive-context-' + className ) ;
if ( ( ! APP . editable || ! editable ) && $el . is ( '.cp-app-drive-context-editable' ) ) { return ; }
if ( ( ! APP . editable || ! editable ) && $el . is ( '.cp-app-drive-context-editable' ) ) { return ; }
if ( filter ( $el , className ) ) { return ; }
$el . parent ( 'li' ) . show ( ) ;
filtered . push ( '.cp-app-drive-context-' + className ) ;
} ) ;
return filtered ;
} ;
var updateContextButton = function ( ) {
if ( manager . isPathIn ( currentPath , [ TRASH ] ) ) {
$driveToolbar . find ( 'cp-app-drive-toolbar-emptytrash' ) . show ( ) ;
} else {
$driveToolbar . find ( 'cp-app-drive-toolbar-emptytrash' ) . hide ( ) ;
}
var $li = findSelectedElements ( ) ;
if ( $li . length === 0 ) {
$li = findDataHolder ( $tree . find ( '.cp-app-drive-element-active' ) ) ;
}
var $button = $driveToolbar . find ( '#cp-app-drive-toolbar-context-mobile' ) ;
if ( $button . length ) { // mobile
if ( $li . length !== 1
|| ! $ . _data ( $li [ 0 ] , 'events' ) . contextmenu
|| $ . _data ( $li [ 0 ] , 'events' ) . contextmenu . length === 0 ) {
$button . hide ( ) ;
return ;
}
$button . show ( ) ;
$button . css ( {
background : '#63b1f7'
} ) ;
window . setTimeout ( function ( ) {
$button . css ( {
background : ''
} ) ;
} , 500 ) ;
return ;
}
// Non mobile
/ *
var $container = $driveToolbar . find ( '#cp-app-drive-toolbar-contextbuttons' ) ;
if ( ! $container . length ) { return ; }
$container . html ( '' ) ;
var $element = $li . length === 1 ? $li : $ ( $li [ 0 ] ) ;
var paths = getSelectedPaths ( $element ) ;
var menuType = $element . data ( 'context' ) ;
if ( ! menuType ) { return ; }
//var actions = [];
var toShow = filterContextMenu ( menuType , paths ) ;
var $actions = $contextMenu . find ( 'a' ) ;
$contextMenu . data ( 'paths' , paths ) ;
$actions = $actions . filter ( function ( i , el ) {
return toShow . some ( function ( className ) { return $ ( el ) . is ( className ) ; } ) ;
} ) ;
$actions . each ( function ( i , el ) {
var $a = $ ( '<button>' , { 'class' : 'cp-app-drive-element' } ) ;
if ( $ ( el ) . attr ( 'data-icon' ) ) {
var font = $ ( el ) . attr ( 'data-icon' ) . indexOf ( 'cptools' ) === 0 ? 'cptools' : 'fa' ;
$a . addClass ( font ) . addClass ( $ ( el ) . attr ( 'data-icon' ) ) ;
$a . attr ( 'title' , $ ( el ) . text ( ) ) ;
} else {
$a . text ( $ ( el ) . text ( ) ) ;
}
$container . append ( $a ) ;
$a . click ( function ( ) { $ ( el ) . click ( ) ; } ) ;
} ) ;
* /
} ;
var scrollTo = function ( $element ) {
// Current scroll position
var st = $content . scrollTop ( ) ;
// Block height
var h = $content . height ( ) ;
// Current top position of the element relative to the scroll position
var pos = Math . round ( $element . offset ( ) . top - $content . position ( ) . top ) ;
// Element height
var eh = $element . outerHeight ( ) ;
// New scroll value
var v = st + pos + eh - h ;
// If the element is completely visile, don't change the scroll position
if ( pos + eh <= h && pos >= 0 ) { return ; }
$content . scrollTop ( v ) ;
} ;
// Add the "selected" class to the "li" corresponding to the clicked element
var onElementClick = APP . onElementClick = function ( e , $element ) {
// If "Ctrl" is pressed, do not remove the current selection
removeInput ( ) ;
$element = findDataHolder ( $element ) ;
// If we're selecting a new element with the left click, hide the menu
if ( e ) { APP . hideMenu ( ) ; }
// Remove the selection if we don't hold ctrl key or if we are right-clicking
if ( ! e || ! e . ctrlKey ) {
removeSelected ( e && e . shiftKey ) ;
}
if ( ! $element . length ) {
log ( Messages . fm _selectError ) ;
return ;
}
scrollTo ( $element ) ;
// Add the selected class to the clicked / right-clicked element
// Remove the class if it already has it
// If ctrlKey, add to the selection
// If shiftKey, select a range of elements
var $elements = $content . find ( '.cp-app-drive-element:not(.cp-app-drive-element-header)' ) ;
var $selection = $elements . filter ( '.cp-app-drive-element-selected' ) ;
if ( typeof sel . startSelected !== "number" || ! e || ( e . ctrlKey && ! e . shiftKey ) ) {
sel . startSelected = $elements . index ( $element [ 0 ] ) ;
sel . oldSelection = [ ] ;
$selection . each ( function ( idx , el ) {
sel . oldSelection . push ( el ) ;
} ) ;
delete sel . endSelected ;
}
if ( e && e . shiftKey ) {
var end = $elements . index ( $element [ 0 ] ) ;
sel . endSelected = end ;
var $el ;
removeSelected ( true ) ;
sel . oldSelection . forEach ( function ( el ) {
if ( ! isElementSelected ( $ ( el ) ) ) {
selectElement ( $ ( el ) ) ;
}
} ) ;
for ( var i = Math . min ( sel . startSelected , sel . endSelected ) ;
i <= Math . max ( sel . startSelected , sel . endSelected ) ;
i ++ ) {
$el = $ ( $elements . get ( i ) ) ;
if ( ! isElementSelected ( $el ) ) {
selectElement ( $el ) ;
}
}
} else {
if ( ! isElementSelected ( $element ) ) {
selectElement ( $element ) ;
} else {
unselectElement ( $element ) ;
}
}
updateContextButton ( ) ;
} ;
// show / hide dropdown separators
var hideSeparators = function ( $menu ) {
var showSep = false ;
var $lastVisibleSep = null ;
$menu . children ( ) . each ( function ( i , el ) {
var $el = $ ( el ) ;
if ( $el . is ( ".dropdown-divider" ) ) {
$el . css ( "display" , showSep ? "list-item" : "none" ) ;
if ( showSep ) { $lastVisibleSep = $el ; }
showSep = false ;
}
else if ( $el . is ( "li" ) && $el . css ( "display" ) !== "none" ) {
showSep = true ;
}
} ) ;
if ( ! showSep && $lastVisibleSep ) { $lastVisibleSep . css ( "display" , "none" ) ; } // remove last divider if no options after
} ;
// prepare and display contextmenu
var displayMenu = function ( e ) {
var $menu = $contextMenu ;
// show / hide submenus
$menu . find ( ".dropdown-submenu" ) . each ( function ( i , el ) {
var $el = $ ( el ) ;
$el . children ( ".dropdown-menu" ) . css ( "display" , "none" ) ;
$el . find ( "li" ) . each ( function ( i , li ) {
if ( $ ( li ) . css ( "display" ) !== "none" ) {
$el . css ( "display" , "block" ) ;
return ;
}
} ) ;
} ) ;
// show / hide separators
$menu . find ( ".dropdown-menu" ) . each ( function ( i , menu ) {
hideSeparators ( $ ( menu ) ) ;
} ) ;
// show contextmenu at cursor position
$menu . css ( { display : "block" } ) ;
if ( APP . mobile ( ) ) {
$menu . css ( {
top : ( $ ( "#cp-app-drive-toolbar-context-mobile" ) . offset ( ) . top + 32 ) + 'px' ,
right : '0px' ,
left : ''
} ) ;
return ;
}
var h = $menu . outerHeight ( ) ;
var w = $menu . outerWidth ( ) ;
var wH = window . innerHeight ;
var wW = window . innerWidth ;
if ( h > wH ) {
$menu . css ( {
top : '0px' ,
bottom : ''
} ) ;
} else if ( e . pageY + h <= wH ) {
$menu . css ( {
top : e . pageY + 'px' ,
bottom : ''
} ) ;
} else {
$menu . css ( {
bottom : '0px' ,
top : ''
} ) ;
}
if ( w > wW ) {
$menu . css ( {
left : '0px' ,
right : ''
} ) ;
} else if ( e . pageX + w <= wW ) {
$menu . css ( {
left : e . pageX + 'px' ,
right : ''
} ) ;
} else {
$menu . css ( {
left : '' ,
right : '0px' ,
} ) ;
}
} ;
// Open the selected context menu on the closest "li" element
var openContextMenu = function ( type ) {
return function ( e ) {
APP . hideMenu ( ) ;
e . stopPropagation ( ) ;
var paths ;
if ( type === 'content' ) {
paths = [ { path : $ ( e . target ) . closest ( '#' + FOLDER _CONTENT _ID ) . data ( 'path' ) } ] ;
if ( ! paths ) { return ; }
removeSelected ( ) ;
} else {
var $element = findDataHolder ( $ ( e . target ) ) ;
// if clicked from tree
var fromTree = $element . closest ( "#cp-app-drive-tree" ) . length ;
if ( fromTree ) {
removeSelected ( ) ;
}
// if clicked on non selected element
if ( ! isElementSelected ( $element ) ) {
removeSelected ( ) ;
}
if ( type === 'trash' && ! $element . data ( 'path' ) ) { return ; }
if ( ! $element . length ) {
logError ( "Unable to locate the .element tag" , e . target ) ;
log ( Messages . fm _contextMenuError ) ;
return false ;
}
if ( ! isElementSelected ( $element ) ) {
selectElement ( $element ) ;
}
paths = getSelectedPaths ( $element ) ;
}
$contextMenu . attr ( 'data-menu-type' , type ) ;
filterContextMenu ( type , paths ) ;
displayMenu ( e ) ;
$ ( ".cp-app-drive-context-noAction" ) . toggle ( $contextMenu . find ( 'li:visible' ) . length === 0 ) ;
$contextMenu . data ( 'paths' , paths ) ;
return false ;
} ;
} ;
var getElementName = function ( path ) {
var file = manager . find ( path ) ;
if ( ! file ) { return ; }
if ( manager . isSharedFolder ( file ) ) {
return manager . getSharedFolderData ( file ) . title ;
}
return manager . getTitle ( file ) ;
} ;
// moveElements is able to move several paths to a new location
var moveElements = function ( paths , newPath , copy , cb ) {
if ( ! APP . editable ) { return ; }
// Cancel drag&drop from TRASH to TRASH
if ( manager . isPathIn ( newPath , [ TRASH ] ) && paths . length && paths [ 0 ] [ 0 ] === TRASH ) {
return ;
}
var newCb = function ( ) {
paths . forEach ( function ( path ) {
LS . moveFoldersOpened ( path , newPath ) ;
} ) ;
cb ( ) ;
} ;
if ( paths . some ( function ( p ) { return manager . comparePath ( newPath , p ) ; } ) ) { return void cb ( ) ; }
manager . move ( paths , newPath , newCb , copy ) ;
} ;
// Delete paths from the drive and/or shared folders (without moving them to the trash)
var deletePaths = function ( paths , pathsList ) {
pathsList = pathsList || [ ] ;
if ( paths ) {
paths . forEach ( function ( p ) { pathsList . push ( p . path ) ; } ) ;
}
var msg = Messages . _getKey ( "fm_removeSeveralPermanentlyDialog" , [ pathsList . length ] ) ;
if ( pathsList . length === 1 ) {
msg = Messages . fm _removePermanentlyDialog ;
}
UI . confirm ( msg , function ( res ) {
$ ( window ) . focus ( ) ;
if ( ! res ) { return ; }
manager . delete ( pathsList , function ( ) {
pathsList . forEach ( LS . removeFoldersOpened ) ;
removeSelected ( ) ;
refresh ( ) ;
} ) ;
} , null , true ) ;
} ;
// Drag & drop
// The data transferred is a stringified JSON containing the path of the dragged element
var onDrag = function ( ev , path ) {
var paths = [ ] ;
var $element = findDataHolder ( $ ( ev . target ) ) ;
if ( $element . hasClass ( 'cp-app-drive-element-selected' ) ) {
var $selected = findSelectedElements ( ) ;
$selected . each ( function ( idx , elmt ) {
var ePath = $ ( elmt ) . data ( 'path' ) ;
if ( ePath ) {
var val = manager . find ( ePath ) ;
if ( ! val ) { return ; } // Error? A ".selected" element is not in the object
paths . push ( {
path : ePath ,
value : {
name : getElementName ( ePath ) ,
el : val
}
} ) ;
}
} ) ;
} else {
removeSelected ( ) ;
selectElement ( $element ) ;
var val = manager . find ( path ) ;
if ( ! val ) { return ; } // The element is not in the object
paths = [ {
path : path ,
value : {
name : getElementName ( path ) ,
el : val
}
} ] ;
}
var data = {
'path' : paths
} ;
ev . dataTransfer . setData ( "text" , stringify ( data ) ) ;
} ;
var findDropPath = function ( target ) {
var $target = $ ( target ) ;
var $el ;
if ( $target . is ( ".cp-app-drive-path-element" ) ) {
$el = $target ;
}
else {
$el = findDataHolder ( $target ) ;
}
var newPath = $el . data ( 'path' ) ;
var dropEl = newPath && manager . find ( newPath ) ;
if ( newPath && manager . isSharedFolder ( dropEl ) ) {
newPath . push ( manager . user . userObject . ROOT ) ;
} else if ( ( ! newPath || manager . isFile ( dropEl ) )
&& $target . parents ( '#cp-app-drive-content' ) ) {
newPath = currentPath ;
}
return newPath ;
} ;
var onFileDrop = APP . onFileDrop = function ( file , e ) {
var ev = {
target : e . target ,
path : findDropPath ( e . target )
} ;
APP . FM . onFileDrop ( file , ev ) ;
} ;
var onDrop = function ( ev ) {
ev . preventDefault ( ) ;
$ ( '.cp-app-drive-element-droppable' ) . removeClass ( 'cp-app-drive-element-droppable' ) ;
var data = ev . dataTransfer . getData ( "text" ) ;
var newPath = findDropPath ( ev . target ) ;
if ( ! newPath ) { return ; }
var sfId = manager . isInSharedFolder ( newPath ) ;
if ( sfId && folders [ sfId ] && folders [ sfId ] . readOnly ) {
return void UI . warn ( Messages . fm _forbidden ) ;
}
// Don't use the normal drop handler for file upload
var fileDrop = ev . dataTransfer . files ;
if ( fileDrop . length ) { return void onFileDrop ( fileDrop , ev ) ; }
var oldPaths = JSON . parse ( data ) . path ;
if ( ! oldPaths ) { return ; }
// A moved element should be removed from its previous location
var movedPaths = [ ] ;
var sharedF = false ;
oldPaths . forEach ( function ( p ) {
movedPaths . push ( p . path ) ;
if ( ! sharedF && manager . isInSharedFolder ( p . path ) ) {
sharedF = true ;
}
} ) ;
if ( sharedF && manager . isPathIn ( newPath , [ TRASH ] ) ) {
// TODO create a key here?
// You can't move to YOUR trash documents stored in a shared folder
// TODO or keep deletePaths: trigger the "Remove from cryptdrive" modal
return void UI . warn ( Messages . error ) ;
//return void deletePaths(null, movedPaths);
}
var copy = false ;
if ( ev . ctrlKey || ( ev . metaKey && APP . isMac ) ) {
copy = true ;
}
if ( movedPaths && movedPaths . length ) {
moveElements ( movedPaths , newPath , copy , refresh ) ;
}
} ;
var addDragAndDropHandlers = function ( $element , path , isFolder , droppable ) {
if ( ! APP . editable ) { return ; }
// "dragenter" is fired for an element and all its children
// "dragleave" may be fired when entering a child
// --> we use pointer-events: none in CSS, but we still need a counter to avoid some issues
// --> We store the number of enter/leave and the element entered and we remove the
// highlighting only when we have left everything
var counter = 0 ;
$element . on ( 'dragstart' , function ( e ) {
e . stopPropagation ( ) ;
counter = 0 ;
onDrag ( e . originalEvent , path ) ;
} ) ;
$element . on ( 'mousedown' , function ( e ) {
e . stopPropagation ( ) ;
} ) ;
// Add drop handlers if we are not in the trash and if the element is a folder
if ( ! droppable || ! isFolder ) { return ; }
$element . on ( 'dragover' , function ( e ) {
e . preventDefault ( ) ;
} ) ;
$element . on ( 'drop' , function ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
onDrop ( e . originalEvent ) ;
} ) ;
$element . on ( 'dragenter' , function ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
counter ++ ;
$element . addClass ( 'cp-app-drive-element-droppable' ) ;
} ) ;
$element . on ( 'dragleave' , function ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
counter -- ;
if ( counter <= 0 ) {
counter = 0 ;
$element . removeClass ( 'cp-app-drive-element-droppable' ) ;
}
} ) ;
} ;
addDragAndDropHandlers ( $content , null , true , true ) ;
$tree . on ( 'drop dragover' , function ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
} ) ;
$driveToolbar . on ( 'drop dragover' , function ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
} ) ;
// In list mode, display metadata from the filesData object
var _addOwnership = function ( $span , $state , data ) {
if ( data && Array . isArray ( data . owners ) && data . owners . indexOf ( edPublic ) !== - 1 ) {
var $owned = $ownedIcon . clone ( ) . appendTo ( $state ) ;
$owned . attr ( 'title' , Messages . fm _padIsOwned ) ;
$span . addClass ( 'cp-app-drive-element-owned' ) ;
} / * else if ( data . owners && data . owners . length ) {
var $owner = $ownerIcon . clone ( ) . appendTo ( $state ) ;
$owner . attr ( 'title' , Messages . fm _padIsOwnedOther ) ;
} * /
} ;
var thumbsUrls = { } ;
var addFileData = function ( element , $element ) {
if ( ! manager . isFile ( element ) ) { return ; }
var data = manager . getFileData ( element ) ;
if ( ! Object . keys ( data ) . length ) {
return true ;
}
var href = data . href || data . roHref ;
if ( ! data ) { return void logError ( "No data for the file" , element ) ; }
var hrefData = Hash . parsePadUrl ( href ) ;
if ( hrefData . type ) {
$element . addClass ( 'cp-border-color-' + hrefData . type ) ;
}
var $state = $ ( '<span>' , { 'class' : 'cp-app-drive-element-state' } ) ;
if ( hrefData . hashData && hrefData . hashData . mode === 'view' ) {
var $ro = $readonlyIcon . clone ( ) . appendTo ( $state ) ;
$ro . attr ( 'title' , Messages . readonly ) ;
}
if ( data . filename && data . filename !== data . title ) {
var $renamed = $renamedIcon . clone ( ) . appendTo ( $state ) ;
$renamed . attr ( 'data-cptippy-html' , 'true' ) ;
$renamed . attr ( 'title' , Messages . _getKey ( 'fm_renamedPad' , [ Util . fixHTML ( data . title ) ] ) ) ;
}
if ( hrefData . hashData && hrefData . hashData . password ) {
var $password = $passwordIcon . clone ( ) . appendTo ( $state ) ;
$password . attr ( 'title' , Messages . fm _passwordProtected || '' ) ;
}
if ( data . expire ) {
var $expire = $expirableIcon . clone ( ) . appendTo ( $state ) ;
$expire . attr ( 'title' , Messages . _getKey ( 'fm_expirablePad' , [ new Date ( data . expire ) . toLocaleString ( ) ] ) ) ;
}
_addOwnership ( $element , $state , data ) ;
var name = manager . getTitle ( element ) ;
// The element with the class '.name' is underlined when the 'li' is hovered
var $name = $ ( '<span>' , { 'class' : 'cp-app-drive-element-name' } ) . text ( name ) ;
$element . append ( $name ) ;
$element . append ( $state ) ;
if ( getViewMode ( ) === 'grid' ) {
$element . attr ( 'title' , name ) ;
}
// display the thumbnail
// if the thumbnail has already been displayed once, do not reload it, keep the same url
if ( thumbsUrls [ element ] ) {
var img = new Image ( ) ;
img . src = thumbsUrls [ element ] ;
$element . prepend ( img ) ;
$ ( img ) . addClass ( 'cp-app-drive-element-grid cp-app-drive-element-thumbnail' ) ;
$ ( img ) . attr ( "draggable" , false ) ;
}
else {
common . displayThumbnail ( href || data . roHref , data . channel , data . password , $element , function ( $thumb ) {
// Called only if the thumbnail exists
// Remove the .hide() added by displayThumnail() because it hides the icon in list mode too
$element . find ( '.cp-icon' ) . removeAttr ( 'style' ) ;
$thumb . addClass ( 'cp-app-drive-element-grid cp-app-drive-element-thumbnail' ) ;
$thumb . attr ( "draggable" , false ) ;
thumbsUrls [ element ] = $thumb [ 0 ] . src ;
} ) ;
}
var type = Messages . type [ hrefData . type ] || hrefData . type ;
var $type = $ ( '<span>' , {
'class' : 'cp-app-drive-element-type cp-app-drive-element-list'
} ) . text ( type ) ;
var $adate = $ ( '<span>' , {
'class' : 'cp-app-drive-element-atime cp-app-drive-element-list'
} ) . text ( getDate ( data . atime ) ) ;
var $cdate = $ ( '<span>' , {
'class' : 'cp-app-drive-element-ctime cp-app-drive-element-list'
} ) . text ( getDate ( data . ctime ) ) ;
$element . append ( $type ) . append ( $adate ) . append ( $cdate ) ;
} ;
var addFolderData = function ( element , key , $span ) {
if ( ! element || ! manager . isFolder ( element ) ) { return ; }
// The element with the class '.name' is underlined when the 'li' is hovered
var $state = $ ( '<span>' , { 'class' : 'cp-app-drive-element-state' } ) ;
var $ro ;
if ( manager . isSharedFolder ( element ) ) {
var data = manager . getSharedFolderData ( element ) ;
var fId = element ;
key = data . title || data . lastTitle || Messages . fm _deletedFolder ;
element = Util . find ( manager , [ 'folders' , element , 'proxy' , manager . user . userObject . ROOT ] ) || { } ;
$span . addClass ( 'cp-app-drive-element-sharedf' ) ;
_addOwnership ( $span , $state , data ) ;
var hrefData = Hash . parsePadUrl ( data . href || data . roHref ) ;
if ( hrefData . hashData && hrefData . hashData . password ) {
var $password = $passwordIcon . clone ( ) . appendTo ( $state ) ;
$password . attr ( 'title' , Messages . fm _passwordProtected || '' ) ;
}
if ( hrefData . hashData && hrefData . hashData . mode === 'view' ) {
$ro = $readonlyIcon . clone ( ) . appendTo ( $state ) ;
$ro . attr ( 'title' , Messages . readonly ) ;
}
if ( files . restrictedFolders [ fId ] ) {
var $restricted = $restrictedIcon . clone ( ) . appendTo ( $state ) ;
$restricted . attr ( 'title' , Messages . fm _restricted ) ;
}
var $shared = $sharedIcon . clone ( ) . appendTo ( $state ) ;
$shared . attr ( 'title' , Messages . fm _canBeShared ) ;
} else if ( $content . data ( 'readOnlyFolder' ) || APP . readOnly ) {
$ro = $readonlyIcon . clone ( ) . appendTo ( $state ) ;
$ro . attr ( 'title' , Messages . readonly ) ;
}
var sf = manager . hasSubfolder ( element ) ;
var hasFiles = manager . hasFile ( element ) ;
var $name = $ ( '<span>' , { 'class' : 'cp-app-drive-element-name' } ) . text ( key ) ;
var $subfolders = $ ( '<span>' , {
'class' : 'cp-app-drive-element-folders cp-app-drive-element-list'
} ) . text ( sf ) ;
var $files = $ ( '<span>' , {
'class' : 'cp-app-drive-element-files cp-app-drive-element-list'
} ) . text ( hasFiles ) ;
var $filler = $ ( '<span>' , {
'class' : 'cp-app-drive-element-filler cp-app-drive-element-list'
} ) ;
if ( getViewMode ( ) === 'grid' ) {
$span . attr ( 'title' , key ) ;
}
$span . append ( $name ) . append ( $state ) . append ( $subfolders ) . append ( $files ) . append ( $filler ) ;
} ;
// This is duplicated in cryptpad-common, it should be unified
var getFileIcon = function ( id ) {
var data = manager . getFileData ( id ) ;
return UI . getFileIcon ( data ) ;
} ;
var getIcon = UI . getIcon ;
var createShareButton = function ( id , $container ) {
var $shareBlock = $ ( '<button>' , {
'class' : 'cp-toolbar-share-button' ,
title : Messages . shareButton
} ) ;
$sharedIcon . clone ( ) . appendTo ( $shareBlock ) ;
$ ( '<span>' ) . text ( Messages . shareButton ) . appendTo ( $shareBlock ) ;
var data = manager . getSharedFolderData ( id ) ;
var parsed = ( data . href && data . href . indexOf ( '#' ) !== - 1 ) ? Hash . parsePadUrl ( data . href ) : { } ;
var roParsed = Hash . parsePadUrl ( data . roHref ) || { } ;
if ( ! parsed . hash && ! roParsed . hash ) { return void console . error ( "Invalid href: " + ( data . href || data . roHref ) ) ; }
var friends = common . getFriends ( ) ;
var ro = folders [ id ] && folders [ id ] . version >= 2 ;
// If we're a viewer and this is an old shared folder (no read-only mode), we
// can't share the read-only URL and we don't have access to the edit one.
// We should hide the share button.
if ( ! data . href && ! ro ) { return ; }
$shareBlock . click ( function ( ) {
Share . getShareModal ( common , {
teamId : APP . team ,
origin : APP . origin ,
pathname : "/drive/" ,
friends : friends ,
title : data . title ,
password : data . password ,
sharedFolder : true ,
common : common ,
hashes : {
editHash : parsed . hash ,
viewHash : ro && roParsed . hash ,
}
} ) ;
} ) ;
$container . append ( $shareBlock ) ;
return $shareBlock ;
} ;
// Create the "li" element corresponding to the file/folder located in "path"
var createElement = function ( path , elPath , root , isFolder ) {
// Forbid drag&drop inside the trash
var isTrash = path [ 0 ] === TRASH ;
var newPath = path . slice ( ) ;
var key ;
var element ;
if ( isTrash && Array . isArray ( elPath ) ) {
key = elPath [ 0 ] ;
elPath . forEach ( function ( k ) { newPath . push ( k ) ; } ) ;
element = manager . find ( newPath ) ;
} else {
key = elPath ;
newPath . push ( key ) ;
element = root [ key ] ;
}
var restricted = files . restrictedFolders [ element ] ;
var isSharedFolder = manager . isSharedFolder ( element ) ;
var $icon = ! isFolder ? getFileIcon ( element ) : undefined ;
var ro = manager . isReadOnlyFile ( element ) ;
// ro undefined means it's an old hash which doesn't support read-only
var roClass = typeof ( ro ) === 'undefined' ? '.cp-app-drive-element-noreadonly' :
ro ? '.cp-app-drive-element-readonly' : '' ;
var liClass = '.cp-app-drive-element-file' ;
var restrictedClass = restricted ? '.cp-app-drive-element-restricted' : '' ;
if ( isSharedFolder ) {
liClass = '.cp-app-drive-element-folder' ;
$icon = $sharedFolderIcon . clone ( ) ;
$icon . css ( "color" , getFolderColor ( path . concat ( elPath ) ) ) ;
} else if ( isFolder ) {
liClass = '.cp-app-drive-element-folder' ;
$icon = manager . isFolderEmpty ( root [ key ] ) ? $folderEmptyIcon . clone ( ) : $folderIcon . clone ( ) ;
$icon . css ( "color" , getFolderColor ( path . concat ( elPath ) ) ) ;
}
var classes = restrictedClass + roClass + liClass ;
var $element = $ ( h ( 'li.cp-app-drive-element.cp-app-drive-element-row' + classes , {
draggable : true
} ) ) ;
$element . data ( 'path' , newPath ) ;
if ( isElementSelected ( $element ) ) {
selectElement ( $element ) ;
}
$element . prepend ( $icon ) . dblclick ( function ( ) {
if ( restricted ) {
UI . warn ( Messages . fm _restricted ) ;
return ;
}
if ( isSharedFolder && ! manager . folders [ element ] ) {
UI . warn ( Messages . fm _deletedFolder ) ;
return ;
}
if ( isFolder ) {
APP . displayDirectory ( newPath ) ;
return ;
}
if ( isTrash ) { return ; }
openFile ( root [ key ] ) ;
} ) ;
var invalid ;
if ( isFolder ) {
invalid = addFolderData ( element , key , $element ) ;
} else {
invalid = addFileData ( element , $element ) ;
}
if ( invalid ) {
return ;
}
$element . find ( '.fa' ) . on ( 'mouseenter' , function ( e ) {
if ( $element [ 0 ] && $element [ 0 ] . _tippy ) {
$element [ 0 ] . _tippy . destroy ( ) ;
}
e . stopPropagation ( ) ;
} ) ;
var droppable = ! isTrash && ! APP . $content . data ( 'readOnlyFolder' ) && ! restricted ;
addDragAndDropHandlers ( $element , newPath , isFolder , droppable ) ;
$element . click ( function ( e ) {
e . stopPropagation ( ) ;
onElementClick ( e , $element ) ;
} ) ;
if ( ! isTrash ) {
$element . on ( 'contextmenu' , openContextMenu ( 'tree' ) ) ;
$element . data ( 'context' , 'tree' ) ;
} else {
$element . contextmenu ( openContextMenu ( 'trash' ) ) ;
$element . data ( 'context' , 'trash' ) ;
}
var isNewFolder = APP . newFolder && manager . comparePath ( newPath , APP . newFolder ) ;
if ( isNewFolder ) {
appStatus . onReady ( function ( ) {
window . setTimeout ( function ( ) { displayRenameInput ( $element , newPath ) ; } , 0 ) ;
} ) ;
delete APP . newFolder ;
}
if ( isSharedFolder && APP . convertedFolder === element ) {
setTimeout ( function ( ) {
var $fakeButton = createShareButton ( element , $ ( '<div>' ) ) ;
if ( ! $fakeButton ) { return ; }
$fakeButton . click ( ) ;
} , 100 ) ;
}
return $element ;
} ;
// Display the full path in the title when displaying a directory from the trash
/ *
var getTrashTitle = function ( path ) {
if ( ! path [ 0 ] || path [ 0 ] !== TRASH ) { return ; }
var title = TRASH _NAME ;
for ( var i = 1 ; i < path . length ; i ++ ) {
if ( i === 3 && path [ i ] === 'element' ) { }
else if ( i === 2 && parseInt ( path [ i ] ) === path [ i ] ) {
if ( path [ i ] !== 0 ) {
title += " [" + path [ i ] + "]" ;
}
} else {
title += " / " + path [ i ] ;
}
}
return title ;
} ; * /
var getPrettyName = function ( name ) {
var pName ;
switch ( name ) {
case ROOT : pName = ROOT _NAME ; break ;
case TRASH : pName = TRASH _NAME ; break ;
case TEMPLATE : pName = TEMPLATE _NAME ; break ;
case FILES _DATA : pName = FILES _DATA _NAME ; break ;
case SEARCH : pName = SEARCH _NAME ; break ;
case RECENT : pName = RECENT _NAME ; break ;
case OWNED : pName = OWNED _NAME ; break ;
case TAGS : pName = TAGS _NAME ; break ;
case SHARED _FOLDER : pName = SHARED _FOLDER _NAME ; break ;
default : pName = name ;
}
return pName ;
} ;
var drivePathOverflowing = function ( ) {
var $container = $ ( ".cp-app-drive-path" ) ;
if ( $container . length ) {
$container . css ( "overflow" , "hidden" ) ;
var overflown = $container [ 0 ] . scrollWidth > $container [ 0 ] . clientWidth ;
$container . css ( "overflow" , "" ) ;
return overflown ;
}
} ;
var collapseDrivePath = function ( ) {
var $container = $ ( ".cp-app-drive-path-inner" ) ;
var $spanCollapse = $ ( ".cp-app-drive-path-collapse" ) ;
$spanCollapse . css ( "display" , "none" ) ;
var $pathElements = $container . find ( ".cp-app-drive-path-element" ) ;
$pathElements . not ( $spanCollapse ) . css ( "display" , "" ) ;
var oneFolder = currentPath . length > 1 + ( currentPath [ 0 ] === SHARED _FOLDER ) ;
if ( oneFolder && drivePathOverflowing ( ) ) {
var collapseLevel = 0 ;
var removeOverflowElement = function ( ) {
if ( drivePathOverflowing ( ) ) {
if ( $pathElements . length <= 3 ) {
return false ;
}
collapseLevel ++ ;
if ( $ ( $pathElements . get ( - 2 ) ) . is ( ".cp-app-drive-path-separator" ) ) {
$ ( $pathElements . get ( - 2 ) ) . css ( "display" , "none" ) ;
$pathElements = $pathElements . not ( $pathElements . get ( - 2 ) ) ;
}
$ ( $pathElements . get ( - 2 ) ) . css ( "display" , "none" ) ;
$pathElements = $pathElements . not ( $pathElements . get ( - 2 ) ) ;
return true ;
}
} ;
currentPath . every ( removeOverflowElement ) ;
$spanCollapse . css ( "display" , "" ) ;
removeOverflowElement ( ) ;
var tipPath = currentPath . slice ( 0 , collapseLevel ) ;
tipPath [ 0 ] = getPrettyName ( tipPath [ 0 ] ) ;
$spanCollapse . attr ( "title" , tipPath . join ( " / " ) ) ;
$spanCollapse [ 0 ] . onclick = function ( ) {
APP . displayDirectory ( LS . getLastOpenedFolder ( ) . slice ( 0 , collapseLevel ) ) ;
} ;
}
} ;
window . addEventListener ( "resize" , collapseDrivePath ) ;
var treeResizeObserver = new MutationObserver ( collapseDrivePath ) ;
treeResizeObserver . observe ( $ ( "#cp-app-drive-tree" ) [ 0 ] , { "attributes" : true } ) ;
// Create the title block with the "parent folder" button
var createTitle = function ( $container , path , noStyle ) {
if ( ! path || path . length === 0 ) { return ; }
var isTrash = manager . isPathIn ( path , [ TRASH ] ) ;
if ( APP . mobile ( ) && ! noStyle ) { // noStyle means title in search result
return $container ;
}
var isVirtual = virtualCategories . indexOf ( path [ 0 ] ) !== - 1 ;
var el = isVirtual ? undefined : manager . find ( path ) ;
path = path [ 0 ] === SEARCH ? path . slice ( 0 , 1 ) : path ;
var isInTrashRoot = manager . isInTrashRoot ( path ) ;
var $outer = $ ( '<div>' , { 'class' : 'cp-app-drive-path' } ) ;
var $inner = $ ( '<div>' , { 'class' : 'cp-app-drive-path-inner' } ) ;
$outer . append ( $inner ) ;
$container . prepend ( $outer ) ;
var skipNext = false ; // When encountering a shared folder, skip a key in the path
path . forEach ( function ( p , idx ) {
if ( isTrash && [ 2 , 3 ] . indexOf ( idx ) !== - 1 ) { return ; }
if ( skipNext ) { skipNext = false ; return ; }
var name = p ;
if ( manager . isFile ( el ) && isInTrashRoot && idx === 1 ) {
idx = 3 ;
}
// Check if the current element is a shared folder. If it is, get its
// name and skip the next itme in the path (it will be "root")
var currentEl = isVirtual ? undefined : manager . find ( path . slice ( 0 , idx + 1 ) ) ;
// If we're in trash root, check if the "element" is a shared folder
if ( isTrash && idx === 1 ) {
currentEl = manager . find ( path . slice ( 0 , idx + 3 ) ) ;
}
// Name and skip next...
// "p === SHARED_FOLDER" for anonymous shared folders
if ( p === SHARED _FOLDER || ( currentEl && manager . isSharedFolder ( currentEl ) ) ) {
name = manager . getSharedFolderData ( currentEl || APP . newSharedFolder ) . title ;
skipNext = true ;
}
var $span = $ ( '<span>' , { 'class' : 'cp-app-drive-path-element' } ) ;
if ( idx < path . length - 1 ) {
if ( ! noStyle ) {
$span . addClass ( 'cp-app-drive-path-clickable' ) ;
$span . click ( function ( e ) {
e . stopPropagation ( ) ;
var sliceEnd = idx + 1 ;
if ( isTrash && idx === 1 ) { sliceEnd = 4 ; } // Make sure we don't show the index or 'element' and 'path'
APP . displayDirectory ( path . slice ( 0 , sliceEnd ) ) ;
} ) ;
}
} else if ( idx > 0 && manager . isFile ( el ) ) {
name = getElementName ( path ) ;
}
$span . data ( "path" , path . slice ( 0 , idx + 1 ) ) ;
addDragAndDropHandlers ( $span , path . slice ( 0 , idx ) , true , true ) ;
if ( idx === 0 ) { name = p === SHARED _FOLDER ? name : getPrettyName ( p ) ; }
else {
var $span2 = $ ( '<span>' , {
'class' : 'cp-app-drive-path-element cp-app-drive-path-separator'
} ) . text ( ' / ' ) ;
$inner . prepend ( $span2 ) ;
}
$span . text ( name ) . prependTo ( $inner ) ;
} ) ;
var $spanCollapse = $ ( '<span>' , {
'class' : 'cp-app-drive-path-element cp-app-drive-path-collapse'
} ) . text ( ' ... ' ) ;
$inner . append ( $spanCollapse ) ;
collapseDrivePath ( ) ;
} ;
var createInfoBox = function ( path ) {
if ( APP . readOnly || $content . data ( 'readOnlyFolder' ) ) { return ; }
var $box = $ ( '<div>' , { 'class' : 'cp-app-drive-content-info-box' } ) ;
var msg ;
switch ( path [ 0 ] ) {
case ROOT :
msg = Messages . fm _info _root ;
break ;
case TEMPLATE :
msg = Messages . fm _info _template ;
break ;
case TRASH :
msg = Messages . fm _info _trash ;
break ;
case FILES _DATA :
msg = Messages . fm _info _allFiles ;
break ;
case RECENT :
msg = Messages . fm _info _recent ;
break ;
case OWNED :
msg = Messages . fm _info _owned ;
break ;
case TAGS :
break ;
default :
msg = undefined ;
}
if ( history . isHistoryMode && history . sfId ) {
// Shared folder history: always display the warning
var sfName = ( manager . getSharedFolderData ( history . sfId ) || { } ) . title || Messages . fm _sharedFolderName ;
msg = Messages . _getKey ( 'fm_info_sharedFolderHistory' , [ sfName ] ) ;
return $ ( common . fixLinks ( $box . html ( msg ) ) ) ;
}
if ( ! APP . loggedIn ) {
msg = APP . newSharedFolder ? Messages . fm _info _sharedFolder : Messages . _getKey ( 'fm_info_anonymous' , [ ApiConfig . inactiveTime || 90 ] ) ;
return $ ( common . fixLinks ( $box . html ( msg ) ) ) ;
}
if ( ! msg || APP . store [ 'hide-info-' + path [ 0 ] ] === '1' ) {
$box . hide ( ) ;
} else {
$box . text ( msg ) ;
var $close = $closeIcon . clone ( ) . css ( {
'cursor' : 'pointer' ,
'margin-left' : '10px' ,
title : Messages . fm _closeInfoBox
} ) . on ( 'click' , function ( ) {
$box . hide ( ) ;
APP . store [ 'hide-info-' + path [ 0 ] ] = '1' ;
localStore . put ( 'hide-info-' + path [ 0 ] , '1' ) ;
} ) ;
$box . prepend ( $close ) ;
}
return $box ;
} ;
// Create the button allowing the user to switch from list to icons modes
var createViewModeButton = function ( $container ) {
var $listButton = $listIcon . clone ( ) ;
var $gridButton = $gridIcon . clone ( ) ;
$listButton . click ( function ( ) {
$gridButton . show ( ) ;
$listButton . hide ( ) ;
setViewMode ( 'list' ) ;
$ ( '#' + FOLDER _CONTENT _ID ) . removeClass ( 'cp-app-drive-content-grid' ) ;
$ ( '#' + FOLDER _CONTENT _ID ) . addClass ( 'cp-app-drive-content-list' ) ;
Feedback . send ( 'DRIVE_LIST_MODE' ) ;
} ) ;
$gridButton . click ( function ( ) {
$listButton . show ( ) ;
$gridButton . hide ( ) ;
setViewMode ( 'grid' ) ;
$ ( '#' + FOLDER _CONTENT _ID ) . addClass ( 'cp-app-drive-content-grid' ) ;
$ ( '#' + FOLDER _CONTENT _ID ) . removeClass ( 'cp-app-drive-content-list' ) ;
Feedback . send ( 'DRIVE_GRID_MODE' ) ;
} ) ;
if ( getViewMode ( ) === 'list' ) {
$listButton . hide ( ) ;
} else {
$gridButton . hide ( ) ;
}
$listButton . attr ( 'title' , Messages . fm _viewListButton ) ;
$gridButton . attr ( 'title' , Messages . fm _viewGridButton ) ;
$container . append ( $listButton ) . append ( $gridButton ) ;
} ;
var emptyTrashModal = function ( ) {
var ownedInTrash = manager . ownedInTrash ( ) ;
var hasOwned = Array . isArray ( ownedInTrash ) && ownedInTrash . length ;
var content = h ( 'p' , [
Messages . fm _emptyTrashDialog ,
hasOwned ? h ( 'br' ) : undefined ,
hasOwned ? UI . setHTML ( h ( 'span' ) , Messages . fm _emptyTrashOwned ) : undefined
] ) ;
var buttons = [ {
className : 'cancel' ,
name : Messages . cancelButton ,
onClick : function ( ) { } ,
keys : [ 27 ]
} ] ;
if ( hasOwned ) {
buttons . push ( {
className : 'danger' ,
iconClass : '.cptools.cptools-destroy' ,
name : Messages . fc _delete _owned ,
onClick : function ( ) {
manager . emptyTrash ( true , refresh ) ;
} ,
keys : [ ]
} ) ;
}
buttons . push ( {
className : 'primary' ,
// We may want to use a new key here
iconClass : '.fa.fa-trash' ,
name : hasOwned ? Messages . fc _remove : Messages . okButton ,
onClick : function ( ) {
manager . emptyTrash ( false , refresh ) ;
} ,
keys : [ 13 ]
} ) ;
var m = UI . dialog . customModal ( content , {
buttons : buttons
} ) ;
UI . openCustomModal ( m ) ;
} ;
var createEmptyTrashButton = function ( ) {
var button = h ( 'button.btn.btn-danger' , [
h ( 'i.fa.' + faTrash ) ,
h ( 'span' , Messages . fc _empty )
] ) ;
$ ( button ) . click ( function ( ) {
emptyTrashModal ( ) ;
} ) ;
return $ ( h ( 'div.cp-app-drive-button' , button ) ) ;
} ;
// Get the upload options
var addSharedFolderModal = function ( cb ) {
var docsHref = common . getBounceURL ( "https://docs.cryptpad.fr/en/user_guide/share_and_access.html#owners" ) ;
// Ask for name, password and owner
var content = h ( 'div' , [
h ( 'h4' , Messages . sharedFolders _create ) ,
h ( 'label' , { for : 'cp-app-drive-sf-name' } , Messages . sharedFolders _create _name ) ,
h ( 'input#cp-app-drive-sf-name' , { type : 'text' , placeholder : Messages . fm _newFolder } ) ,
h ( 'label' , { for : 'cp-app-drive-sf-password' } , Messages . sharedFolders _create _password ) ,
UI . passwordInput ( { id : 'cp-app-drive-sf-password' } ) ,
h ( 'span' , {
style : 'display:flex;align-items:center;justify-content:space-between'
} , [
UI . createCheckbox ( 'cp-app-drive-sf-owned' , Messages . sharedFolders _create _owned , true ) ,
UI . createHelper ( docsHref , Messages . creation _owned1 ) // TODO
] ) ,
] ) ;
$ ( content ) . find ( '#cp-app-drive-sf-name' ) . keydown ( function ( e ) {
if ( e . which === 13 ) {
UI . findOKButton ( ) . click ( ) ;
}
} ) ;
UI . confirm ( content , function ( yes ) {
if ( ! yes ) { return void cb ( ) ; }
// Get the values
var newName = $ ( content ) . find ( '#cp-app-drive-sf-name' ) . val ( ) ;
var password = $ ( content ) . find ( '#cp-app-drive-sf-password' ) . val ( ) || undefined ;
var owned = $ ( content ) . find ( '#cp-app-drive-sf-owned' ) . is ( ':checked' ) ;
cb ( {
name : newName ,
password : password ,
owned : owned
} ) ;
} ) ;
} ;
var getNewPadTypes = function ( ) {
var arr = [ ] ;
AppConfig . availablePadTypes . forEach ( function ( type ) {
if ( type === 'drive' ) { return ; }
if ( type === 'teams' ) { return ; }
if ( type === 'contacts' ) { return ; }
if ( type === 'todo' ) { return ; }
if ( type === 'file' ) { return ; }
if ( type === 'accounts' ) { return ; }
if ( ! APP . loggedIn && AppConfig . registeredOnlyTypes &&
AppConfig . registeredOnlyTypes . indexOf ( type ) !== - 1 ) {
return ;
}
arr . push ( type ) ;
} ) ;
return arr ;
} ;
var showUploadFilesModal = function ( ) {
var $input = $ ( '<input>' , {
'type' : 'file' ,
'style' : 'display: none;' ,
'multiple' : 'multiple'
} ) . on ( 'change' , function ( e ) {
var files = Util . slice ( e . target . files ) ;
files . forEach ( function ( file ) {
var ev = {
target : $content [ 0 ] ,
path : findDropPath ( $content [ 0 ] )
} ;
APP . FM . handleFile ( file , ev ) ;
} ) ;
} ) ;
$input . click ( ) ;
} ;
// create the folder structure before to upload files from folder
var uploadFolder = function ( fileList ) {
var currentFolder = currentPath ;
// create an array of all the files relative path
var files = Array . prototype . map . call ( fileList , function ( file ) {
return {
file : file ,
path : file . webkitRelativePath . split ( "/" ) ,
} ;
} ) ;
// if folder name already exist in drive, rename it
var uploadedFolderName = files [ 0 ] . path [ 0 ] ;
var availableName = manager . user . userObject . getAvailableName ( manager . find ( currentFolder ) , uploadedFolderName ) ;
// ask for folder name and files options, then upload all the files!
APP . FM . showFolderUploadModal ( availableName , function ( folderUploadOptions ) {
if ( ! folderUploadOptions ) { return ; }
// verfify folder name is possible, and update files path
availableName = manager . user . userObject . getAvailableName ( manager . find ( currentFolder ) , folderUploadOptions . folderName ) ;
if ( uploadedFolderName !== availableName ) {
files . forEach ( function ( file ) {
file . path [ 0 ] = availableName ;
} ) ;
}
// uploadSteps is an array of objects {folders: [], files: []}, containing all the folders and files to create safely
// at the index i + 1, the files and folders are children of the folders at the index i
var maxSteps = files . reduce ( function ( max , file ) { return Math . max ( max , file . path . length ) ; } , 0 ) ;
var uploadSteps = [ ] ;
for ( var i = 0 ; i < maxSteps ; i ++ ) {
uploadSteps [ i ] = {
folders : [ ] ,
files : [ ] ,
} ;
}
files . forEach ( function ( file ) {
// add steps to create subfolders containing file
for ( var depth = 0 ; depth < file . path . length - 1 ; depth ++ ) {
var subfolderStr = file . path . slice ( 0 , depth + 1 ) . join ( "/" ) ;
if ( uploadSteps [ depth ] . folders . indexOf ( subfolderStr ) === - 1 ) {
uploadSteps [ depth ] . folders . push ( subfolderStr ) ;
}
}
// add step to upload file (one step later than the step of its direct parent folder)
uploadSteps [ file . path . length - 1 ] . files . push ( file ) ;
} ) ;
// add folders, then add files when theirs folders have been created
// wait for the folders to be created to go to the next step (don't wait for the files)
var stepByStep = function ( uploadSteps , i ) {
if ( i >= uploadSteps . length ) { return ; }
nThen ( function ( waitFor ) {
// add folders
uploadSteps [ i ] . folders . forEach ( function ( folder ) {
var folderPath = folder . split ( "/" ) ;
var parentFolder = currentFolder . concat ( folderPath . slice ( 0 , - 1 ) ) ;
var folderName = folderPath . slice ( - 1 ) ;
manager . addFolder ( parentFolder , folderName , waitFor ( refresh ) ) ;
} ) ;
// upload files
uploadSteps [ i ] . files . forEach ( function ( file ) {
var ev = {
target : $content [ 0 ] ,
path : currentFolder . concat ( file . path . slice ( 0 , - 1 ) ) ,
} ;
APP . FM . handleFile ( file . file , ev , folderUploadOptions ) ;
} ) ;
} ) . nThen ( function ( ) {
stepByStep ( uploadSteps , i + 1 ) ;
} ) ;
} ;
stepByStep ( uploadSteps , 0 ) ;
} ) ;
} ;
var showUploadFolderModal = function ( ) {
var $input = $ ( '<input>' , {
'type' : 'file' ,
'style' : 'display: none;' ,
'multiple' : 'multiple' ,
'webkitdirectory' : true ,
} ) . on ( 'change' , function ( e ) {
uploadFolder ( e . target . files ) ;
} ) ;
$input . click ( ) ;
} ;
var addNewPadHandlers = function ( $block , isInRoot ) {
// Handlers
if ( isInRoot ) {
var onCreated = function ( err , info ) {
if ( err ) {
if ( err === E _OVER _LIMIT ) {
return void UI . alert ( Messages . pinLimitDrive , null , true ) ;
}
return void UI . alert ( Messages . fm _error _cantPin ) ;
}
APP . newFolder = info . newPath ;
refresh ( ) ;
} ;
$block . find ( 'a.cp-app-drive-new-folder, li.cp-app-drive-new-folder' )
. click ( function ( ) {
manager . addFolder ( currentPath , null , onCreated ) ;
} ) ;
if ( ! APP . disableSF && ! manager . isInSharedFolder ( currentPath ) ) {
$block . find ( 'a.cp-app-drive-new-shared-folder, li.cp-app-drive-new-shared-folder' )
. click ( function ( ) {
addSharedFolderModal ( function ( obj ) {
if ( ! obj ) { return ; }
manager . addSharedFolder ( currentPath , obj , refresh ) ;
} ) ;
} ) ;
}
$block . find ( 'a.cp-app-drive-new-fileupload, li.cp-app-drive-new-fileupload' ) . click ( showUploadFilesModal ) ;
$block . find ( 'a.cp-app-drive-new-folderupload, li.cp-app-drive-new-folderupload' ) . click ( showUploadFolderModal ) ;
}
$block . find ( 'a.cp-app-drive-new-doc, li.cp-app-drive-new-doc' )
. click ( function ( ) {
var type = $ ( this ) . attr ( 'data-type' ) || 'pad' ;
var path = manager . isPathIn ( currentPath , [ TRASH ] ) ? '' : currentPath ;
openIn ( type , path , APP . team ) ;
} ) ;
} ;
var createNewButton = function ( isInRoot , $container ) {
if ( ! APP . editable ) { return ; }
if ( ! APP . loggedIn ) { return ; } // Anonymous users can use the + menu in the toolbar
if ( ! manager . isPathIn ( currentPath , [ ROOT , 'hrefArray' ] ) ) { return ; }
// Create dropdown
var options = [ ] ;
if ( isInRoot ) {
options . push ( {
tag : 'a' ,
attributes : { 'class' : 'cp-app-drive-new-folder' } ,
content : $ ( '<div>' ) . append ( $folderIcon . clone ( ) ) . html ( ) + Messages . fm _folder
} ) ;
if ( ! APP . disableSF && ! manager . isInSharedFolder ( currentPath ) ) {
options . push ( {
tag : 'a' ,
attributes : { 'class' : 'cp-app-drive-new-shared-folder' } ,
content : $ ( '<div>' ) . append ( $sharedFolderIcon . clone ( ) ) . html ( ) + Messages . fm _sharedFolder
} ) ;
}
options . push ( { tag : 'hr' } ) ;
options . push ( {
tag : 'a' ,
attributes : { 'class' : 'cp-app-drive-new-fileupload' } ,
content : $ ( '<div>' ) . append ( getIcon ( 'fileupload' ) ) . html ( ) + Messages . uploadButton
} ) ;
if ( APP . allowFolderUpload ) {
options . push ( {
tag : 'a' ,
attributes : { 'class' : 'cp-app-drive-new-folderupload' } ,
content : $ ( '<div>' ) . append ( getIcon ( 'folderupload' ) ) . html ( ) + Messages . uploadFolderButton
} ) ;
}
options . push ( { tag : 'hr' } ) ;
}
getNewPadTypes ( ) . forEach ( function ( type ) {
var attributes = {
'class' : 'cp-app-drive-new-doc' ,
'data-type' : type ,
'href' : '#'
} ;
options . push ( {
tag : 'a' ,
attributes : attributes ,
content : $ ( '<div>' ) . append ( getIcon ( type ) ) . html ( ) + Messages . type [ type ]
} ) ;
} ) ;
var $plusIcon = $ ( '<div>' ) . append ( $ ( '<span>' , { 'class' : 'fa fa-plus' } ) ) ;
var dropdownConfig = {
text : $plusIcon . html ( ) + '<span>' + Messages . fm _newButton + '</span>' ,
options : options ,
feedback : 'DRIVE_NEWPAD_LOCALFOLDER' ,
common : common
} ;
var $block = UIElements . createDropdown ( dropdownConfig ) ;
// Custom style:
$block . find ( 'button' ) . addClass ( 'cp-app-drive-toolbar-new' ) ;
addNewPadHandlers ( $block , isInRoot ) ;
$container . append ( $block ) ;
} ;
var SORT _FOLDER _DESC = 'sortFoldersDesc' ;
var SORT _FILE _BY = 'sortFilesBy' ;
var SORT _FILE _DESC = 'sortFilesDesc' ;
var getSortFileDesc = function ( ) {
return APP . store [ SORT _FILE _DESC ] + "" === "true" ;
} ;
var getSortFolderDesc = function ( ) {
return APP . store [ SORT _FOLDER _DESC ] + "" === "true" ;
} ;
var onSortByClick = function ( ) {
var $span = $ ( this ) ;
var value ;
if ( $span . hasClass ( 'cp-app-drive-sort-foldername' ) ) {
value = getSortFolderDesc ( ) ;
APP . store [ SORT _FOLDER _DESC ] = value ? false : true ;
localStore . put ( SORT _FOLDER _DESC , value ? false : true ) ;
refresh ( ) ;
return ;
}
value = APP . store [ SORT _FILE _BY ] ;
var descValue = getSortFileDesc ( ) ;
if ( $span . hasClass ( 'cp-app-drive-sort-filename' ) ) {
if ( value === '' ) {
descValue = descValue ? false : true ;
} else {
descValue = false ;
value = '' ;
}
} else {
[ 'cp-app-drive-element-type' ,
'cp-app-drive-element-atime' , 'cp-app-drive-element-ctime' ] . some ( function ( c ) {
if ( $span . hasClass ( c ) ) {
var nValue = c . replace ( /cp-app-drive-element-/ , '' ) ;
if ( value === nValue ) { descValue = descValue ? false : true ; }
else {
// atime and ctime should be ordered in a desc order at the first click
value = nValue ;
descValue = value !== 'title' ;
}
return true ;
}
} ) ;
}
APP . store [ SORT _FILE _BY ] = value ;
APP . store [ SORT _FILE _DESC ] = descValue ;
localStore . put ( SORT _FILE _BY , value ) ;
localStore . put ( SORT _FILE _DESC , descValue ) ;
refresh ( ) ;
} ;
var addFolderSortIcon = function ( $list ) {
var $icon = $sortAscIcon . clone ( ) ;
if ( getSortFolderDesc ( ) ) {
$icon = $sortDescIcon . clone ( ) ;
}
if ( typeof ( APP . store [ SORT _FOLDER _DESC ] ) !== "undefined" ) {
$list . find ( '.cp-app-drive-sort-foldername' ) . addClass ( 'cp-app-drive-sort-active' ) . prepend ( $icon ) ;
}
} ;
var getSortDropdown = function ( ) {
var $fhSort = $ ( h ( 'span.cp-dropdown-container.cp-app-drive-element-sort.cp-app-drive-sort-clickable' ) ) ;
var options = [ {
tag : 'a' ,
attributes : { 'class' : 'cp-app-drive-element-type' } ,
content : '<i class="fa fa-minus"></i>' + Messages . fm _type
} , {
tag : 'a' ,
attributes : { 'class' : 'cp-app-drive-element-atime' } ,
content : '<i class="fa fa-minus"></i>' + Messages . fm _lastAccess
} , {
tag : 'a' ,
attributes : { 'class' : 'cp-app-drive-element-ctime' } ,
content : '<i class="fa fa-minus"></i>' + Messages . fm _creation
} ] ;
var dropdownConfig = {
text : '' , // Button initial text
options : options , // Entries displayed in the menu
container : $fhSort ,
left : true ,
noscroll : true ,
common : common
} ;
var $sortBlock = UIElements . createDropdown ( dropdownConfig ) ;
$sortBlock . find ( 'button' ) . append ( h ( 'span.fa.fa-sort-amount-desc' ) ) . append ( h ( 'span' , Messages . fm _sort ) ) ;
$sortBlock . on ( 'click' , 'a' , onSortByClick ) ;
return $fhSort ;
} ;
var getFolderListHeader = function ( clickable , small ) {
var $fohElement = $ ( '<li>' , {
'class' : 'cp-app-drive-element-header cp-app-drive-element-list'
} ) ;
var clickCls = clickable ? 'cp-app-drive-sort-clickable ' : '' ;
var onClick = clickable ? onSortByClick : function ( ) { } ;
//var $fohElement = $('<span>', {'class': 'element'}).appendTo($folderHeader);
var $fhIcon = $ ( '<span>' , { 'class' : 'cp-app-drive-content-icon' } ) ;
var $name = $ ( '<span>' , {
'class' : 'cp-app-drive-element-name cp-app-drive-sort-foldername ' + clickCls
} ) . text ( Messages . fm _folderName ) . click ( onClick ) ;
var $state = $ ( '<span>' , { 'class' : 'cp-app-drive-element-state' } ) ;
var $subfolders , $files ;
if ( ! small ) {
$subfolders = $ ( '<span>' , {
'class' : 'cp-app-drive-element-folders cp-app-drive-element-list'
} ) . text ( Messages . fm _numberOfFolders ) ;
$files = $ ( '<span>' , {
'class' : 'cp-app-drive-element-files cp-app-drive-element-list'
} ) . text ( Messages . fm _numberOfFiles ) ;
}
var $filler = $ ( '<span>' , {
'class' : 'cp-app-drive-element-filler cp-app-drive-element-list'
} ) ;
$fohElement . append ( $fhIcon ) . append ( $name ) . append ( $state )
. append ( $subfolders ) . append ( $files ) . append ( $filler ) ;
if ( clickable ) { addFolderSortIcon ( $fohElement ) ; }
return $fohElement ;
} ;
var addFileSortIcon = function ( $list ) {
var $icon = $sortAscIcon . clone ( ) ;
if ( getSortFileDesc ( ) ) {
$icon = $sortDescIcon . clone ( ) ;
}
var classSorted ;
if ( APP . store [ SORT _FILE _BY ] === '' ) { classSorted = 'cp-app-drive-sort-filename' ; }
else if ( APP . store [ SORT _FILE _BY ] ) { classSorted = 'cp-app-drive-element-' + APP . store [ SORT _FILE _BY ] ; }
if ( classSorted ) {
$list . find ( '.' + classSorted ) . addClass ( 'cp-app-drive-sort-active' ) . prepend ( $icon ) . find ( 'i' ) . hide ( ) ;
}
} ;
var getFileListHeader = function ( clickable ) {
var $fihElement = $ ( '<li>' , {
'class' : 'cp-app-drive-element-header cp-app-drive-element-list'
} ) ;
var clickCls = clickable ? 'cp-app-drive-sort-clickable ' : '' ;
var onClick = clickable ? onSortByClick : function ( ) { } ;
//var $fihElement = $('<span>', {'class': 'element'}).appendTo($fileHeader);
var $fhIcon = $ ( '<span>' , { 'class' : 'cp-app-drive-content-icon' } ) ;
var $fhName = $ ( '<span>' , {
'class' : 'cp-app-drive-element-name cp-app-drive-sort-filename ' + clickCls
} ) . text ( Messages . fm _fileName ) . click ( onClick ) ;
var $fhSort = clickable ? getSortDropdown ( ) : undefined ;
var $fhState = $ ( '<span>' , { 'class' : 'cp-app-drive-element-state' } ) ;
var $fhType = $ ( '<span>' , {
'class' : 'cp-app-drive-element-type cp-app-drive-element-list ' + clickCls
} ) . text ( Messages . fm _type ) . click ( onClick ) ;
var $fhAdate = $ ( '<span>' , {
'class' : 'cp-app-drive-element-atime cp-app-drive-element-list ' + clickCls
} ) . text ( Messages . fm _lastAccess ) . click ( onClick ) ;
var $fhCdate = $ ( '<span>' , {
'class' : 'cp-app-drive-element-ctime cp-app-drive-element-list ' + clickCls
} ) . text ( Messages . fm _creation ) . click ( onClick ) ;
// If displayTitle is false, it means the "name" is the title, so do not display the "name" header
$fihElement . append ( $fhIcon ) . append ( $fhName ) . append ( $fhSort ) . append ( $fhState ) . append ( $fhType ) ;
$fihElement . append ( $fhAdate ) . append ( $fhCdate ) ;
if ( clickable ) { addFileSortIcon ( $fihElement ) ; }
return $fihElement ;
} ;
var sortElements = function ( folder , path , oldkeys , prop , asc , useId ) {
var root = path && manager . find ( path ) ;
if ( path [ 0 ] === SHARED _FOLDER ) {
path = path . slice ( 1 ) ;
root = Util . find ( folders [ APP . newSharedFolder ] , path ) ;
}
var test = folder ? manager . isFolder : manager . isFile ;
var keys = oldkeys . filter ( function ( e ) {
return useId ? test ( e ) : ( path && test ( root [ e ] ) ) ;
} ) ;
if ( keys . length < 2 ) { return keys ; }
var mult = asc ? 1 : - 1 ;
var getProp = function ( _el ) {
var el = useId ? _el : root [ _el ] ;
var sfId = ( el && el . root && el . key ) ? el . root [ el . key ] : el ;
if ( folder && el && manager . isSharedFolder ( sfId ) ) {
var sfData = manager . getSharedFolderData ( sfId ) ;
var title = sfData . title || sfData . lastTitle || el ;
return String ( title ) . toLowerCase ( ) ;
} else if ( folder ) {
return String ( ( el && el . key ) || _el ) . toLowerCase ( ) ;
}
var data = manager . getFileData ( el ) ;
if ( ! data ) { return '' ; }
if ( prop === 'type' ) {
var hrefData = Hash . parsePadUrl ( data . href || data . roHref ) ;
return hrefData . type ;
}
if ( prop === 'atime' || prop === 'ctime' ) {
return typeof ( data [ prop ] ) === "number" ? data [ prop ] : new Date ( data [ prop ] ) ;
}
return ( manager . getTitle ( el ) || "" ) . toLowerCase ( ) ;
} ;
var props = { } ;
keys . forEach ( function ( k ) {
var uid = k ;
if ( typeof ( k ) === "object" ) {
uid = k . uid = Util . uid ( ) ;
}
props [ uid ] = getProp ( k ) ;
} ) ;
keys . sort ( function ( a , b ) {
var _a = props [ ( a && a . uid ) || a ] ;
var _b = props [ ( b && b . uid ) || b ] ;
if ( _a < _b ) { return mult * - 1 ; }
if ( _b < _a ) { return mult ; }
return 0 ;
} ) ;
return keys ;
} ;
var sortTrashElements = function ( folder , oldkeys , prop , asc ) {
var test = folder ? manager . isFolder : manager . isFile ;
var keys = oldkeys . filter ( function ( e ) {
return test ( e . element ) ;
} ) ;
if ( keys . length < 2 ) { return keys ; }
var mult = asc ? 1 : - 1 ;
var getProp = function ( el , prop ) {
if ( prop && ! folder ) {
var element = el . element ;
var e = manager . getFileData ( element ) ;
if ( ! e ) {
e = {
href : el ,
title : Messages . fm _noname ,
atime : 0 ,
ctime : 0
} ;
}
if ( prop === 'type' ) {
var hrefData = Hash . parsePadUrl ( e . href || e . roHref ) ;
return hrefData . type ;
}
if ( prop === 'atime' || prop === 'ctime' ) {
return new Date ( e [ prop ] ) ;
}
}
return ( el . name || "" ) . toLowerCase ( ) ;
} ;
keys . sort ( function ( a , b ) {
if ( getProp ( a , prop ) < getProp ( b , prop ) ) { return mult * - 1 ; }
if ( getProp ( a , prop ) > getProp ( b , prop ) ) { return mult * 1 ; }
return 0 ;
} ) ;
return keys ;
} ;
// Create the ghost icon to add pads/folders
var createNewPadIcons = function ( $block , isInRoot ) {
var $container = $ ( '<div>' ) ;
if ( isInRoot ) {
// Folder
var $element1 = $ ( '<li>' , {
'class' : 'cp-app-drive-new-folder cp-app-drive-element-row ' +
'cp-app-drive-element-grid'
} ) . prepend ( $folderIcon . clone ( ) ) . appendTo ( $container ) ;
$element1 . append ( $ ( '<span>' , { 'class' : 'cp-app-drive-new-name' } )
. text ( Messages . fm _folder ) ) ;
// Shared Folder
if ( ! APP . disableSF && ! manager . isInSharedFolder ( currentPath ) ) {
var $element3 = $ ( '<li>' , {
'class' : 'cp-app-drive-new-shared-folder cp-app-drive-element-row ' +
'cp-app-drive-element-grid'
} ) . prepend ( $sharedFolderIcon . clone ( ) ) . appendTo ( $container ) ;
$element3 . append ( $ ( '<span>' , { 'class' : 'cp-app-drive-new-name' } )
. text ( Messages . fm _sharedFolder ) ) ;
}
// Upload file
var $elementFileUpload = $ ( '<li>' , {
'class' : 'cp-app-drive-new-fileupload cp-app-drive-element-row ' +
'cp-app-drive-element-grid'
} ) . prepend ( getIcon ( 'fileupload' ) ) . appendTo ( $container ) ;
$elementFileUpload . append ( $ ( '<span>' , { 'class' : 'cp-app-drive-new-name' } )
. text ( Messages . uploadButton ) ) ;
// Upload folder
if ( APP . allowFolderUpload ) {
var $elementFolderUpload = $ ( '<li>' , {
'class' : 'cp-app-drive-new-folderupload cp-app-drive-element-row ' +
'cp-app-drive-element-grid'
} ) . prepend ( getIcon ( 'folderupload' ) ) . appendTo ( $container ) ;
$elementFolderUpload . append ( $ ( '<span>' , { 'class' : 'cp-app-drive-new-name' } )
. text ( Messages . uploadFolderButton ) ) ;
}
}
// Pads
getNewPadTypes ( ) . forEach ( function ( type ) {
var $element = $ ( '<li>' , {
'class' : 'cp-app-drive-new-doc cp-app-drive-element-row ' +
'cp-app-drive-element-grid'
} ) . prepend ( getIcon ( type ) ) . appendTo ( $container ) ;
$element . append ( $ ( '<span>' , { 'class' : 'cp-app-drive-new-name' } )
. text ( Messages . type [ type ] ) ) ;
$element . attr ( 'data-type' , type ) ;
} ) ;
$container . find ( '.cp-app-drive-element-row' ) . click ( function ( ) {
$block . hide ( ) ;
} ) ;
return $container ;
} ;
var createGhostIcon = function ( $list ) {
if ( APP . $content . data ( 'readOnlyFolder' ) || ! APP . editable ) { return ; }
var isInRoot = currentPath [ 0 ] === ROOT ;
var $element = $ ( '<li>' , {
'class' : 'cp-app-drive-element-row cp-app-drive-new-ghost'
} ) . prepend ( $addIcon . clone ( ) ) . appendTo ( $list ) ;
$element . append ( $ ( '<span>' , { 'class' : 'cp-app-drive-element-name' } )
. text ( Messages . fm _newFile ) ) ;
$element . click ( function ( ) {
var modal = UI . createModal ( {
id : 'cp-app-drive-new-ghost-dialog' ,
$body : $ ( 'body' )
} ) ;
var $modal = modal . $modal ;
var $title = $ ( '<h3>' ) . text ( Messages . fm _newFile ) ;
var $description = $ ( '<p>' ) . text ( Messages . fm _newButtonTitle ) ;
$modal . find ( '.cp-modal' ) . append ( $title ) ;
$modal . find ( '.cp-modal' ) . append ( $description ) ;
var $content = createNewPadIcons ( $modal , isInRoot ) ;
$modal . find ( '.cp-modal' ) . append ( $content ) ;
window . setTimeout ( function ( ) { modal . show ( ) ; } ) ;
addNewPadHandlers ( $modal , isInRoot ) ;
} ) ;
} ;
// Drive content toolbar
var checkCollapseButton = function ( ) {
APP . $collapseButton . removeClass ( 'cp-toolbar-button-active' ) ;
if ( APP . $tree . is ( ':visible' ) ) {
APP . $collapseButton . addClass ( 'cp-toolbar-button-active' ) ;
}
} ;
var collapseTreeButton = function ( ) {
APP . $collapseButton = APP . $collapseButton || common . createButton ( '' , true , {
text : Messages . drive _treeButton ,
name : 'files' ,
icon : 'fa-hdd-o' ,
drawer : false ,
} ) ;
checkCollapseButton ( ) ;
APP . toolbar . $bottomL . append ( APP . $collapseButton ) ;
APP . $collapseButton . off ( 'click' ) . on ( 'click' , function ( ) {
APP . $tree . toggle ( ) ;
checkCollapseButton ( ) ;
} ) ;
} ;
var createToolbar = function ( ) {
var $toolbar = APP . toolbar . $bottom ;
APP . toolbar . $bottomL . html ( '' ) ;
APP . toolbar . $bottomR . html ( '' ) ;
if ( APP . histConfig && ( APP . loggedIn || ! APP . newSharedFolder ) ) {
// ANON_SHARED_FOLDER
var $hist = common . createButton ( 'history' , true , { histConfig : APP . histConfig } ) ;
APP . toolbar . $bottomR . append ( $hist ) ;
}
if ( APP . $burnThisDrive ) {
APP . toolbar . $bottomR . append ( APP . $burnThisDrive ) ;
}
collapseTreeButton ( ) ;
return $toolbar ;
} ;
// Unsorted element are represented by "href" in an array: they don't have a filename
// and they don't hav a hierarchical structure (folder/subfolders)
var displayHrefArray = function ( $container , rootName , draggable ) {
var unsorted = files [ rootName ] ;
if ( unsorted . length ) {
var $fileHeader = getFileListHeader ( true ) ;
$container . append ( $fileHeader ) ;
}
var keys = unsorted ;
var sortBy = APP . store [ SORT _FILE _BY ] ;
sortBy = sortBy === "" ? sortBy = 'name' : sortBy ;
var sortedFiles = sortElements ( false , [ rootName ] , keys , sortBy , ! getSortFileDesc ( ) , true ) ;
sortedFiles . forEach ( function ( id ) {
var file = manager . getFileData ( id ) ;
if ( ! file ) {
//debug("Unsorted or template returns an element not present in filesData: ", href);
file = { title : Messages . fm _noname } ;
//return;
}
var idx = files [ rootName ] . indexOf ( id ) ;
var $icon = getFileIcon ( id ) ;
var ro = manager . isReadOnlyFile ( id ) ;
// ro undefined mens it's an old hash which doesn't support read-only
var roClass = typeof ( ro ) === 'undefined' ? ' cp-app-drive-element-noreadonly' :
ro ? ' cp-app-drive-element-readonly' : '' ;
var $element = $ ( '<li>' , {
'class' : 'cp-app-drive-element cp-app-drive-element-file cp-app-drive-element-row' + roClass ,
draggable : draggable
} ) ;
var path = [ rootName , idx ] ;
$element . data ( 'path' , path ) ;
if ( isElementSelected ( $element ) ) {
selectElement ( $element ) ;
}
$element . prepend ( $icon ) . dblclick ( function ( ) {
openFile ( id ) ;
} ) ;
addFileData ( id , $element ) ;
$element . click ( function ( e ) {
e . stopPropagation ( ) ;
onElementClick ( e , $element ) ;
} ) ;
$element . contextmenu ( openContextMenu ( 'default' ) ) ;
$element . data ( 'context' , 'default' ) ;
if ( draggable ) {
addDragAndDropHandlers ( $element , path , false , false ) ;
}
$container . append ( $element ) ;
} ) ;
createGhostIcon ( $container ) ;
} ;
var displayAllFiles = function ( $container ) {
if ( AppConfig . disableAnonymousStore && ! APP . loggedIn ) {
$container . append ( Messages . anonymousStoreDisabled ) ;
return ;
}
var allfiles = files [ FILES _DATA ] ;
if ( Object . keys ( allfiles || { } ) . length === 0 ) {
createGhostIcon ( $container ) ;
return ;
}
var $fileHeader = getFileListHeader ( true ) ;
$container . append ( $fileHeader ) ;
var keys = manager . getFiles ( [ FILES _DATA ] ) ;
var sortedFiles = sortElements ( false , [ FILES _DATA ] , keys , APP . store [ SORT _FILE _BY ] , ! getSortFileDesc ( ) , true ) ;
sortedFiles . forEach ( function ( id ) {
var $icon = getFileIcon ( id ) ;
var ro = manager . isReadOnlyFile ( id ) ;
// ro undefined maens it's an old hash which doesn't support read-only
var roClass = typeof ( ro ) === 'undefined' ? ' cp-app-drive-element-noreadonly' :
ro ? ' cp-app-drive-element-readonly' : '' ;
var $element = $ ( '<li>' , {
'class' : 'cp-app-drive-element cp-app-drive-element-row' + roClass
} ) ;
$element . prepend ( $icon ) . dblclick ( function ( ) {
openFile ( id ) ;
} ) ;
addFileData ( id , $element ) ;
$element . data ( 'path' , [ FILES _DATA , id ] ) ;
$element . data ( 'element' , id ) ;
$element . click ( function ( e ) {
e . stopPropagation ( ) ;
onElementClick ( e , $element ) ;
} ) ;
$element . contextmenu ( openContextMenu ( 'default' ) ) ;
$element . data ( 'context' , 'default' ) ;
$container . append ( $element ) ;
} ) ;
createGhostIcon ( $container ) ;
} ;
var displayTrashRoot = function ( $list , $folderHeader , $fileHeader ) {
var filesList = [ ] ;
var root = files [ TRASH ] ;
var isEmpty = true ;
// Elements in the trash are JS arrays (several elements can have the same name)
Object . keys ( root ) . forEach ( function ( key ) {
if ( ! Array . isArray ( root [ key ] ) ) {
logError ( "Trash element has a wrong type" , root [ key ] ) ;
return ;
}
root [ key ] . forEach ( function ( el , idx ) {
if ( ! manager . isFile ( el . element ) && ! manager . isFolder ( el . element ) ) { return ; }
var spath = [ key , idx , 'element' ] ;
filesList . push ( {
element : el . element ,
spath : spath ,
name : key
} ) ;
} ) ;
isEmpty = false ;
} ) ;
if ( ! isEmpty ) {
var $empty = createEmptyTrashButton ( ) ;
$content . append ( $empty ) ;
}
var sortedFolders = sortTrashElements ( true , filesList , null , ! getSortFolderDesc ( ) ) ;
var sortedFiles = sortTrashElements ( false , filesList , APP . store [ SORT _FILE _BY ] , ! getSortFileDesc ( ) ) ;
if ( manager . hasSubfolder ( root , true ) ) { $list . append ( $folderHeader ) ; }
sortedFolders . forEach ( function ( f ) {
var $element = createElement ( [ TRASH ] , f . spath , root , true ) ;
$list . append ( $element ) ;
} ) ;
if ( manager . hasFile ( root , true ) ) { $list . append ( $fileHeader ) ; }
sortedFiles . forEach ( function ( f ) {
var $element = createElement ( [ TRASH ] , f . spath , root , false ) ;
$list . append ( $element ) ;
} ) ;
} ;
APP . Search = { } ;
var displaySearch = function ( $list , value ) {
var search = APP . Search ;
var $div = $ ( '<div>' , { 'id' : 'cp-app-drive-search' , 'class' : 'cp-unselectable' } ) ;
$searchIcon . clone ( ) . appendTo ( $div ) ;
var $spinnerContainer = $ ( h ( 'div.cp-app-drive-search-spinner' ) ) ;
var spinner = UI . makeSpinner ( $spinnerContainer ) ;
var $input = APP . Search . $input = $ ( '<input>' , {
id : 'cp-app-drive-search-input' ,
placeholder : Messages . fm _searchName ,
type : 'text' ,
draggable : false ,
tabindex : 1 ,
} ) . keyup ( function ( e ) {
var lastValue = search . value ;
search . value = $input . val ( ) . trim ( ) ;
if ( lastValue === search . value ) { return ; }
if ( search . to ) { window . clearTimeout ( search . to ) ; }
if ( search . value === "" ) {
search . cursor = 0 ;
APP . displayDirectory ( [ SEARCH ] ) ;
return ;
}
spinner . spin ( ) ;
if ( e . which === 13 ) {
var newLocation = [ SEARCH , $input . val ( ) ] ;
search . cursor = $input [ 0 ] . selectionStart ;
if ( ! manager . comparePath ( newLocation , currentPath . slice ( ) ) ) {
APP . displayDirectory ( newLocation ) ;
}
return ;
}
if ( e . which === 27 ) {
$input . val ( '' ) ;
search . cursor = 0 ;
APP . displayDirectory ( [ SEARCH ] ) ;
return ;
}
if ( $input . val ( ) ) {
if ( ! $input . hasClass ( 'cp-app-drive-search-active' ) ) {
$input . addClass ( 'cp-app-drive-search-active' ) ;
}
} else {
$input . removeClass ( 'cp-app-drive-search-active' ) ;
}
search . to = window . setTimeout ( function ( ) {
var newLocation = [ SEARCH , $input . val ( ) ] ;
search . cursor = $input [ 0 ] . selectionStart ;
if ( ! manager . comparePath ( newLocation , currentPath . slice ( ) ) ) {
APP . displayDirectory ( newLocation ) ;
}
} , 500 ) ;
} ) . on ( 'click mousedown mouseup' , function ( e ) {
e . stopPropagation ( ) ;
} ) . val ( value || '' ) . appendTo ( $div ) ;
if ( value ) { $input . addClass ( 'cp-app-drive-search-active' ) ; }
$input [ 0 ] . selectionStart = search . cursor || 0 ;
$input [ 0 ] . selectionEnd = search . cursor || 0 ;
var cancel = h ( 'span.fa.fa-times.cp-app-drive-search-cancel' , { title : Messages . cancel } ) ;
cancel . addEventListener ( 'click' , function ( ) {
$input . val ( '' ) ;
search . cursor = 0 ;
APP . displayDirectory ( [ SEARCH ] ) ;
} ) ;
$div . append ( cancel ) ;
$list . append ( $div ) ;
$spinnerContainer . appendTo ( $list ) ;
setTimeout ( function ( ) {
$input . focus ( ) ;
} ) ;
if ( typeof ( value ) === "string" && value . trim ( ) ) {
spinner . spin ( ) ;
} else {
return ;
}
setTimeout ( function ( ) {
//$list.closest('#cp-app-drive-content-folder').addClass('cp-app-drive-content-list');
var filesList = manager . search ( value ) ;
if ( ! filesList . length ) {
$list . append ( h ( 'div.cp-app-drive-search-noresult' , Messages . fm _noResult ) ) ;
spinner . hide ( ) ;
return ;
}
var sortable = { } ;
var sortableFolders = [ ] ;
filesList . forEach ( function ( r ) {
// if r.id === null, then it's a folder, not a file
r . paths . forEach ( function ( path ) {
if ( ! r . inSharedFolder &&
APP . hideDuplicateOwned && manager . isDuplicateOwned ( path ) ) { return ; }
var _path = path . slice ( ) ;
var key = path . pop ( ) ;
var root = manager . find ( path ) ;
var obj = {
path : path ,
_path : _path ,
key : key ,
root : root ,
data : r . data
} ;
if ( manager . isFolder ( root [ key ] ) ) {
sortableFolders . push ( obj ) ;
return ;
}
sortable [ root [ key ] ] = obj ;
} ) ;
} ) ;
var _folders = sortElements ( true , [ ROOT ] , sortableFolders , null , ! getSortFolderDesc ( ) , true ) ;
var sortableKeys = Object . keys ( sortable ) . map ( Number ) ;
var _files = sortElements ( false , [ ROOT ] , sortableKeys , APP . store [ SORT _FILE _BY ] , ! getSortFileDesc ( ) , true ) ;
var addEl = function ( obj , folder ) {
var $element = createElement ( obj . path , obj . key , obj . root , folder ) ;
$element . addClass ( 'cp-app-drive-element-notrash cp-app-drive-search-result' ) ;
$element . off ( 'contextmenu' ) ;
$element . contextmenu ( openContextMenu ( 'default' ) ) ;
$element . data ( 'context' , 'default' ) ;
if ( folder ) {
$element . find ( '.cp-app-drive-element-list' ) . css ( {
visibility : 'hidden'
} ) . text ( '' ) ;
}
if ( manager . isPathIn ( obj . _path , [ 'hrefArray' ] ) ) {
obj . _path . pop ( ) ;
obj . _path . push ( obj . data . title ) ;
}
var $path = $ ( '<span>' , {
'class' : 'cp-app-drive-search-path'
} ) . appendTo ( $element . find ( '.cp-app-drive-element-name' ) ) ;
createTitle ( $path , obj . _path ) ;
$list . append ( $element ) ;
} ;
if ( _folders . length ) { getFolderListHeader ( true , true ) . appendTo ( $list ) ; }
_folders . forEach ( function ( el ) {
var obj = el ;
addEl ( obj , true ) ;
} ) ;
if ( _files . length ) { getFileListHeader ( true ) . appendTo ( $list ) ; }
_files . forEach ( function ( el ) {
var obj = sortable [ el ] ;
addEl ( obj , false ) ;
} ) ;
setTimeout ( collapseDrivePath ) ;
spinner . hide ( ) ;
} ) ;
} ;
var displayRecent = function ( $list ) {
var filesList = manager . getRecentPads ( ) ;
var limit = 20 ;
var now = new Date ( ) ;
var last1 = new Date ( now ) ;
last1 . setDate ( last1 . getDate ( ) - 1 ) ;
var last7 = new Date ( now ) ;
last7 . setDate ( last7 . getDate ( ) - 7 ) ;
var last28 = new Date ( now ) ;
last28 . setDate ( last28 . getDate ( ) - 28 ) ;
var header7 , header28 , headerOld ;
var i = 0 ;
var channels = [ ] ;
$list . append ( h ( 'li.cp-app-drive-element-separator' , h ( 'span' , Messages . drive _active1Day ) ) ) ;
filesList . some ( function ( arr ) {
var id = arr [ 0 ] ;
var file = arr [ 1 ] ;
if ( ! file || ! file . atime ) { return ; }
if ( file . atime <= last28 && i >= limit ) {
return true ;
}
var paths = manager . findFile ( id ) ;
if ( ! paths . length ) { return ; }
var path = paths [ 0 ] ;
if ( manager . isPathIn ( path , [ TRASH ] ) ) { return ; }
if ( channels . indexOf ( file . channel ) !== - 1 ) { return ; }
channels . push ( file . channel ) ;
if ( ! header7 && file . atime < last1 ) {
$list . append ( h ( 'li.cp-app-drive-element-separator' , h ( 'span' , Messages . drive _active7Days ) ) ) ;
header7 = true ;
}
if ( ! header28 && file . atime < last7 ) {
$list . append ( h ( 'li.cp-app-drive-element-separator' , h ( 'span' , Messages . drive _active28Days ) ) ) ;
header28 = true ;
}
if ( ! headerOld && file . atime < last28 ) {
$list . append ( h ( 'li.cp-app-drive-element-separator' , h ( 'span' , Messages . drive _activeOld ) ) ) ;
headerOld = true ;
}
// Display the pad
/ *
var $icon = getFileIcon ( id ) ;
var ro = manager . isReadOnlyFile ( id ) ;
// ro undefined means it's an old hash which doesn't support read-only
var roClass = typeof ( ro ) === 'undefined' ? ' cp-app-drive-element-noreadonly' :
ro ? ' cp-app-drive-element-readonly' : '' ;
var $element = $ ( '<li>' , {
'class' : 'cp-app-drive-element cp-app-drive-element-notrash cp-app-drive-element-file cp-app-drive-element-row' + roClass ,
} ) ; * /
var parentPath = path . slice ( ) ;
var key = parentPath . pop ( ) ;
var root = manager . find ( parentPath ) ;
var $element = createElement ( parentPath , key , root ) ;
$element . off ( 'contextmenu' ) . contextmenu ( openContextMenu ( 'default' ) ) ;
$element . data ( 'context' , 'default' ) ;
$list . append ( $element ) ;
i ++ ;
} ) ;
} ;
// Owned pads category
var displayOwned = function ( $container ) {
var list = manager . getOwnedPads ( ) ;
if ( list . length === 0 ) { return ; }
var $fileHeader = getFileListHeader ( true ) ;
$container . append ( $fileHeader ) ;
var sortedFiles = sortElements ( false , false , list , APP . store [ SORT _FILE _BY ] , ! getSortFileDesc ( ) , true ) ;
sortedFiles . forEach ( function ( id ) {
var paths = manager . findFile ( id ) ;
if ( ! paths . length ) { return ; }
var path = paths [ 0 ] ;
var $icon = getFileIcon ( id ) ;
var ro = manager . isReadOnlyFile ( id ) ;
// ro undefined maens it's an old hash which doesn't support read-only
var roClass = typeof ( ro ) === 'undefined' ? ' cp-app-drive-element-noreadonly' :
ro ? ' cp-app-drive-element-readonly' : '' ;
var $element = $ ( '<li>' , {
'class' : 'cp-app-drive-element cp-app-drive-element-notrash ' +
'cp-app-drive-element-file cp-app-drive-element-row' + roClass
} ) ;
$element . prepend ( $icon ) . dblclick ( function ( ) {
openFile ( id ) ;
} ) ;
addFileData ( id , $element ) ;
$element . data ( 'path' , path ) ;
$element . data ( 'element' , id ) ;
$element . click ( function ( e ) {
e . stopPropagation ( ) ;
onElementClick ( e , $element ) ;
} ) ;
$element . contextmenu ( openContextMenu ( 'default' ) ) ;
$element . data ( 'context' , 'default' ) ;
$container . append ( $element ) ;
} ) ;
} ;
// Tags category
var displayTags = function ( $container ) {
var list = manager . getTagsList ( ) ;
if ( Object . keys ( list ) . length === 0 ) { return ; }
var sortedTags = Object . keys ( list ) ;
sortedTags . sort ( function ( a , b ) {
return list [ b ] - list [ a ] ;
} ) ;
var lines = [
h ( 'tr' , [
h ( 'th' , Messages . fm _tags _name ) ,
h ( 'th' , Messages . fm _tags _used )
] )
] ;
sortedTags . forEach ( function ( tag ) {
var tagLink = h ( 'a' , { href : '#' } , '#' + tag ) ;
$ ( tagLink ) . click ( function ( ) {
if ( displayedCategories . indexOf ( SEARCH ) !== - 1 ) {
APP . displayDirectory ( [ SEARCH , '#' + tag ] ) ;
}
} ) ;
lines . push ( h ( 'tr' , [
h ( 'td' , tagLink ) ,
h ( 'td.cp-app-drive-tags-used' , list [ tag ] )
] ) ) ;
} ) ;
$ ( h ( 'li.cp-app-drive-tags-list' , h ( 'table' , lines ) ) ) . appendTo ( $container ) ;
} ;
// ANON_SHARED_FOLDER
// Display a shared folder for anon users (read-only)
var displaySharedFolder = function ( $list ) {
if ( currentPath . length === 1 ) {
currentPath . push ( ROOT ) ;
}
var fId = APP . newSharedFolder ;
var data = folders [ fId ] ;
var $folderHeader = getFolderListHeader ( true ) ;
var $fileHeader = getFileListHeader ( true ) ;
var path = currentPath . slice ( 1 ) ;
var root = Util . find ( data , path ) ;
var realPath = [ ROOT , SHARED _FOLDER ] . concat ( path ) ;
if ( manager . hasSubfolder ( root ) ) { $list . append ( $folderHeader ) ; }
// display sub directories
var keys = Object . keys ( root ) ;
var sortedFolders = sortElements ( true , realPath , keys , null , ! getSortFolderDesc ( ) ) ;
var sortedFiles = sortElements ( false , realPath , keys , APP . store [ SORT _FILE _BY ] , ! getSortFileDesc ( ) ) ;
sortedFolders . forEach ( function ( key ) {
if ( manager . isFile ( root [ key ] ) ) { return ; }
var $element = createElement ( realPath , key , root , true ) ;
$element . appendTo ( $list ) ;
} ) ;
if ( manager . hasFile ( root ) ) { $list . append ( $fileHeader ) ; }
// display files
sortedFiles . forEach ( function ( key ) {
if ( manager . isFolder ( root [ key ] ) ) { return ; }
var $element = createElement ( realPath , key , root , false ) ;
if ( ! $element ) { return ; }
$element . appendTo ( $list ) ;
} ) ;
} ;
// Display the selected directory into the content part (rightside)
// NOTE: Elements in the trash are not using the same storage structure as the others
var _displayDirectory = function ( path , force ) {
if ( APP . closed || ( APP . $content && ! $ . contains ( document . documentElement , APP . $content [ 0 ] ) ) ) { return ; }
APP . hideMenu ( ) ;
if ( ! APP . editable ) { debug ( "Read-only mode" ) ; }
if ( ! appStatus . isReady && ! force ) { return ; }
// Fix path obvious issues
if ( ! path || path . length === 0 ) {
// Only Trash and Root are available in not-owned files manager
if ( ! path || displayedCategories . indexOf ( path [ 0 ] ) === - 1 ) {
log ( Messages . fm _categoryError ) ;
}
if ( ! APP . loggedIn && APP . newSharedFolder ) {
// ANON_SHARED_FOLDER
path = [ SHARED _FOLDER , ROOT ] ;
} else {
path = [ ROOT ] ;
}
}
if ( APP . loggedIn && path [ 0 ] === FILES _DATA ) {
path = [ ROOT ] ;
}
// Get path data
appStatus . ready ( false ) ;
currentPath = path ;
var s = $content . scrollTop ( ) || 0 ;
$content . html ( "" ) ;
sel . $selectBox = $ ( '<div>' , { 'class' : 'cp-app-drive-content-select-box' } )
. appendTo ( $content ) ;
var isInRoot = manager . isPathIn ( path , [ ROOT ] ) ;
var inTrash = manager . isPathIn ( path , [ TRASH ] ) ;
var isTrashRoot = manager . comparePath ( path , [ TRASH ] ) ;
var isTemplate = manager . comparePath ( path , [ TEMPLATE ] ) ;
var isAllFiles = manager . comparePath ( path , [ FILES _DATA ] ) ;
var isVirtual = virtualCategories . indexOf ( path [ 0 ] ) !== - 1 ;
var isSearch = path [ 0 ] === SEARCH ;
var isTags = path [ 0 ] === TAGS ;
// ANON_SHARED_FOLDER
var isSharedFolder = path [ 0 ] === SHARED _FOLDER && APP . newSharedFolder ;
if ( isSharedFolder && path . length < 2 ) {
path = [ SHARED _FOLDER , 'root' ] ;
currentPath = path ;
}
// Make sure the path is valid
var root = isVirtual ? undefined : manager . find ( path ) ;
if ( manager . isSharedFolder ( root ) ) {
// ANON_SHARED_FOLDER
path . push ( manager . user . userObject . ROOT ) ;
root = manager . find ( path ) ;
if ( ! root ) { return ; }
}
if ( ! isVirtual && typeof ( root ) === "undefined" ) {
log ( Messages . fm _unknownFolderError ) ;
debug ( "Unable to locate the selected directory: " , path ) ;
if ( path . length === 1 && path [ 0 ] === ROOT ) {
// Somehow we can't display ROOT. We should abort now because we'll
// end up in an infinite loop
return void UI . warn ( Messages . fm _error _cantPin ) ; // Internal server error, please reload...
}
var parentPath = path . slice ( ) ;
parentPath . pop ( ) ;
_displayDirectory ( parentPath , true ) ;
return ;
}
if ( ! isSearch ) { delete APP . Search . oldLocation ; }
// Display the tree and build the content
APP . resetTree ( ) ;
LS . setLastOpenedFolder ( path ) ;
createToolbar ( path ) ;
if ( ! isSearch ) { createTitle ( $content , path ) ; }
var $info = createInfoBox ( path ) ;
var $dirContent = $ ( '<div>' , { id : FOLDER _CONTENT _ID } ) ;
$dirContent . data ( 'path' , path ) ;
if ( ! isTags ) {
$dirContent . addClass ( getViewModeClass ( isSearch ) ) ;
if ( ! isSearch ) {
createViewModeButton ( APP . toolbar . $bottomR ) ;
}
}
var $list = $ ( '<ul>' ) . appendTo ( $dirContent ) ;
var sfId = manager . isInSharedFolder ( currentPath ) ;
// Restricted folder? display ROOT instead
if ( sfId && files . restrictedFolders [ sfId ] ) {
_displayDirectory ( [ ROOT ] , true ) ;
return ;
}
var readOnlyFolder = false ;
// If the shared folder is offline, add the "DISCONNECTED" banner, otherwise
// use the normal "editable" behavior (based on drive offline or history mode)
if ( sfId && manager . folders [ sfId ] . offline ) {
setEditable ( false , false , true ) ;
} else {
setEditable ( true , false , true ) ;
}
if ( APP . readOnly ) {
// Read-only drive (team?)
$content . prepend ( $readOnly . clone ( ) ) ;
} else if ( sfId && folders [ sfId ] && folders [ sfId ] . readOnly ) {
// If readonly shared folder...
$content . prepend ( $readOnly . clone ( ) ) ;
readOnlyFolder = true ;
}
$content . data ( 'readOnlyFolder' , readOnlyFolder ) ;
if ( ! readOnlyFolder ) {
createNewButton ( isInRoot , APP . toolbar . $bottomL ) ;
}
if ( APP . mobile ( ) ) {
var $context = $ ( '<button>' , {
id : 'cp-app-drive-toolbar-context-mobile'
} ) ;
$context . append ( $ ( '<span>' , { 'class' : 'fa fa-caret-down' } ) ) ;
$context . appendTo ( APP . toolbar . $bottomR ) ;
$context . click ( function ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
var $li = findSelectedElements ( ) ;
if ( $li . length !== 1 ) {
$li = findDataHolder ( $tree . find ( '.cp-toolbar-button-active' ) ) ;
}
// Close if already opened
if ( $ ( '.cp-contextmenu:visible' ) . length ) {
APP . hideMenu ( ) ;
return ;
}
if ( ! $li . length ) {
return void $dirContent . contextmenu ( ) ;
}
// Open the menu
$li . contextmenu ( ) ;
} ) ;
} else {
var $contextButtons = $ ( '<span>' , { 'id' : 'cp-app-drive-toolbar-contextbuttons' } ) ;
$contextButtons . appendTo ( APP . toolbar . $bottomR ) ;
}
updateContextButton ( ) ;
var $folderHeader = getFolderListHeader ( true ) ;
var $fileHeader = getFileListHeader ( true ) ;
if ( isTemplate ) {
displayHrefArray ( $list , path [ 0 ] , true ) ;
} else if ( isAllFiles ) {
displayAllFiles ( $list ) ;
} else if ( isTrashRoot ) {
displayTrashRoot ( $list , $folderHeader , $fileHeader ) ;
} else if ( isSearch ) {
displaySearch ( $list , path [ 1 ] ) ;
} else if ( path [ 0 ] === RECENT ) {
displayRecent ( $list ) ;
} else if ( path [ 0 ] === OWNED ) {
displayOwned ( $list ) ;
} else if ( isTags ) {
displayTags ( $list ) ;
} else if ( isSharedFolder ) {
// ANON_SHARED_FOLDER
displaySharedFolder ( $list ) ;
} else {
if ( ! inTrash ) { $dirContent . contextmenu ( openContextMenu ( 'content' ) ) ; }
if ( manager . hasSubfolder ( root ) ) { $list . append ( $folderHeader ) ; }
// display sub directories
var keys = Object . keys ( root ) ;
var sortedFolders = sortElements ( true , path , keys , null , ! getSortFolderDesc ( ) ) ;
var sortedFiles = sortElements ( false , path , keys , APP . store [ SORT _FILE _BY ] , ! getSortFileDesc ( ) ) ;
sortedFolders . forEach ( function ( key ) {
if ( manager . isFile ( root [ key ] ) ) { return ; }
var $element = createElement ( path , key , root , true ) ;
$element . appendTo ( $list ) ;
} ) ;
if ( manager . hasFile ( root ) ) { $list . append ( $fileHeader ) ; }
// display files
sortedFiles . forEach ( function ( key ) {
if ( manager . isFolder ( root [ key ] ) ) { return ; }
var p = path . slice ( ) ;
p . push ( key ) ;
if ( APP . hideDuplicateOwned && manager . isDuplicateOwned ( p ) ) { return ; }
var $element = createElement ( path , key , root , false ) ;
if ( ! $element ) { return ; }
$element . appendTo ( $list ) ;
} ) ;
if ( ! inTrash ) { createGhostIcon ( $list ) ; }
}
$content . append ( $info ) . append ( $dirContent ) ;
/ * v a r $ t r u n c a t e d = $ ( ' < s p a n > ' , { ' c l a s s ' : ' c p - a p p - d r i v e - e l e m e n t - t r u n c a t e d ' } ) . t e x t ( ' . . . ' ) ;
$content . find ( '.cp-app-drive-element' ) . each ( function ( idx , el ) {
var $name = $ ( el ) . find ( '.cp-app-drive-element-name' ) ;
if ( $name . length === 0 ) { return ; }
if ( $name [ 0 ] . scrollHeight > $name [ 0 ] . clientHeight ) {
var $tr = $truncated . clone ( ) ;
$tr . attr ( 'title' , $name . text ( ) ) ;
$ ( el ) . append ( $tr ) ;
}
} ) ; * /
// If the selected element is not visible, scroll to make it visible, otherwise scroll to
// the previous scroll position
var $sel = findSelectedElements ( ) ;
if ( $sel . length ) {
var _top = $sel [ 0 ] . getBoundingClientRect ( ) . top ;
var _topContent = $content [ 0 ] . getBoundingClientRect ( ) . top ;
if ( ( _topContent + s + $content . height ( ) - 20 ) < _top ) {
$sel [ 0 ] . scrollIntoView ( ) ;
} else {
$content . scrollTop ( s ) ;
}
} else {
$content . scrollTop ( s ) ;
}
delete APP . convertedFolder ;
appStatus . ready ( true ) ;
} ;
var displayDirectory = APP . displayDirectory = function ( path , force ) {
if ( APP . closed || ( APP . $content && ! $ . contains ( document . documentElement , APP . $content [ 0 ] ) ) ) { return ; }
if ( history . isHistoryMode ) {
return void _displayDirectory ( path , force ) ;
}
if ( ! manager . comparePath ( currentPath , path ) ) {
removeSelected ( ) ;
}
updateObject ( sframeChan , proxy , function ( ) {
copyObjectValue ( files , proxy . drive ) ;
updateSharedFolders ( sframeChan , manager , files , folders , function ( ) {
_displayDirectory ( path , force ) ;
} ) ;
} ) ;
} ;
var createTreeElement = function ( name , $icon , path , draggable , droppable , collapsable , active , isSharedFolder ) {
var $name = $ ( '<span>' , { 'class' : 'cp-app-drive-element' } ) . text ( name ) ;
$icon . css ( "color" , isSharedFolder ? getFolderColor ( path . slice ( 0 , - 1 ) ) : getFolderColor ( path ) ) ;
var $collapse ;
if ( collapsable ) {
$collapse = $expandIcon . clone ( ) ;
}
var $elementRow = $ ( '<span>' , { 'class' : 'cp-app-drive-element-row' } ) . append ( $collapse ) . append ( $icon ) . append ( $name ) . click ( function ( e ) {
e . stopPropagation ( ) ;
if ( isSharedFolder && ! manager . folders [ isSharedFolder ] ) {
UI . warn ( Messages . fm _deletedFolder ) ;
return ;
}
if ( files . restrictedFolders [ isSharedFolder ] ) {
UI . warn ( Messages . fm _restricted ) ;
return ;
}
APP . displayDirectory ( path ) ;
} ) ;
if ( files . restrictedFolders [ isSharedFolder ] ) {
$elementRow . addClass ( 'cp-app-drive-element-restricted' ) ;
}
if ( isSharedFolder ) {
var sfData = manager . getSharedFolderData ( isSharedFolder ) ;
_addOwnership ( $elementRow , $ ( ) , sfData ) ;
}
var $element = $ ( '<li>' ) . append ( $elementRow ) ;
if ( draggable ) { $elementRow . attr ( 'draggable' , true ) ; }
if ( collapsable ) {
$element . addClass ( 'cp-app-drive-element-collapsed' ) ;
$collapse . click ( function ( e ) {
e . stopPropagation ( ) ;
if ( $element . hasClass ( 'cp-app-drive-element-collapsed' ) ) {
// It is closed, open it
$element . removeClass ( 'cp-app-drive-element-collapsed' ) ;
LS . setFolderOpened ( path , true ) ;
$collapse . removeClass ( 'fa-plus-square-o' ) ;
$collapse . addClass ( 'fa-minus-square-o' ) ;
} else {
// Collapse the folder
$element . addClass ( 'cp-app-drive-element-collapsed' ) ;
LS . setFolderOpened ( path , false ) ;
$collapse . removeClass ( 'fa-minus-square-o' ) ;
$collapse . addClass ( 'fa-plus-square-o' ) ;
// Change the current opened folder if it was collapsed
if ( manager . isSubpath ( currentPath , path ) ) {
displayDirectory ( path ) ;
}
}
} ) ;
if ( LS . wasFolderOpened ( path ) ||
( manager . isSubpath ( currentPath , path ) && path . length < currentPath . length ) ) {
$collapse . click ( ) ;
}
}
var dataPath = isSharedFolder ? path . slice ( 0 , - 1 ) : path ;
$elementRow . data ( 'path' , dataPath ) ;
addDragAndDropHandlers ( $elementRow , dataPath , true , droppable ) ;
if ( active ) {
$elementRow . addClass ( 'cp-app-drive-element-active cp-leftside-active' ) ;
}
return $element ;
} ;
var createTree = function ( $container , path ) {
var root = manager . find ( path ) ;
// don't try to display what doesn't exist
if ( ! root ) { return ; }
// Display the root element in the tree
var displayingRoot = manager . comparePath ( [ ROOT ] , path ) ;
if ( displayingRoot ) {
var isRootOpened = manager . comparePath ( [ ROOT ] , currentPath ) ;
var $rootIcon = manager . isFolderEmpty ( files [ ROOT ] ) ?
( isRootOpened ? $folderOpenedEmptyIcon : $folderEmptyIcon ) :
( isRootOpened ? $folderOpenedIcon : $folderIcon ) ;
var $rootElement = createTreeElement ( ROOT _NAME , $rootIcon . clone ( ) , [ ROOT ] , false , true , true , isRootOpened ) ;
if ( ! manager . hasSubfolder ( root ) ) {
$rootElement . find ( '.cp-app-drive-icon-expcol' ) . css ( 'visibility' , 'hidden' ) ;
}
$rootElement . addClass ( 'cp-app-drive-tree-root' ) ;
$rootElement . find ( '>.cp-app-drive-element-row' )
. contextmenu ( openContextMenu ( 'tree' ) ) ;
$ ( '<ul>' , { 'class' : 'cp-app-drive-tree-docs' } )
. append ( $rootElement ) . appendTo ( $container ) ;
$container = $rootElement ;
} else if ( manager . isFolderEmpty ( root ) ) { return ; }
// Display root content
var $list = $ ( '<ul>' ) . appendTo ( $container ) ;
var keys = Object . keys ( root ) . sort ( function ( a , b ) {
var newA = manager . isSharedFolder ( root [ a ] ) ?
manager . getSharedFolderData ( root [ a ] ) . title : a ;
var newB = manager . isSharedFolder ( root [ b ] ) ?
manager . getSharedFolderData ( root [ b ] ) . title : b ;
return newA < newB ? - 1 :
( newA === newB ? 0 : 1 ) ;
} ) ;
keys . forEach ( function ( key ) {
// Do not display files in the menu
if ( ! manager . isFolder ( root [ key ] ) ) { return ; }
var newPath = path . slice ( ) ;
newPath . push ( key ) ;
var isSharedFolder = manager . isSharedFolder ( root [ key ] ) && root [ key ] ;
var sfId = manager . isInSharedFolder ( newPath ) || ( isSharedFolder && root [ key ] ) ;
var $icon , isCurrentFolder , subfolder ;
if ( isSharedFolder ) {
// Fix path
newPath . push ( manager . user . userObject . ROOT ) ;
isCurrentFolder = manager . comparePath ( newPath , currentPath ) ;
// Subfolders?
var newRoot = Util . find ( manager , [ 'folders' , sfId , 'proxy' , manager . user . userObject . ROOT ] ) || { } ;
subfolder = manager . hasSubfolder ( newRoot ) ;
// Fix name
key = manager . getSharedFolderData ( sfId ) . title || Messages . fm _deletedFolder ;
// Fix icon
$icon = isCurrentFolder ? $sharedFolderOpenedIcon : $sharedFolderIcon ;
isSharedFolder = sfId ;
} else {
var isEmpty = manager . isFolderEmpty ( root [ key ] ) ;
subfolder = manager . hasSubfolder ( root [ key ] ) ;
isCurrentFolder = manager . comparePath ( newPath , currentPath ) ;
$icon = isEmpty ?
( isCurrentFolder ? $folderOpenedEmptyIcon : $folderEmptyIcon ) :
( isCurrentFolder ? $folderOpenedIcon : $folderIcon ) ;
}
var f = folders [ sfId ] ;
var editable = ! ( f && f . readOnly ) ;
var $element = createTreeElement ( key , $icon . clone ( ) , newPath , true , editable ,
subfolder , isCurrentFolder , isSharedFolder ) ;
$element . appendTo ( $list ) ;
$element . find ( '>.cp-app-drive-element-row' ) . contextmenu ( openContextMenu ( 'tree' ) ) ;
if ( isSharedFolder ) {
$element . find ( '>.cp-app-drive-element-row' )
. addClass ( 'cp-app-drive-element-sharedf' ) ;
}
if ( sfId && ! editable ) {
$element . attr ( 'data-ro' , true ) ;
}
if ( ! subfolder ) { return ; }
createTree ( $element , newPath ) ;
} ) ;
} ;
var createTrash = function ( $container , path ) {
var $icon = manager . isFolderEmpty ( files [ TRASH ] ) ? $trashEmptyIcon . clone ( ) : $trashIcon . clone ( ) ;
var isOpened = manager . comparePath ( path , currentPath ) ;
var $trashElement = createTreeElement ( TRASH _NAME , $icon , [ TRASH ] , false , true , false , isOpened ) ;
$trashElement . addClass ( 'cp-app-drive-tree-root' ) ;
$trashElement . find ( '>.cp-app-drive-element-row' )
. contextmenu ( openContextMenu ( 'trashtree' ) ) ;
var $trashList = $ ( '<ul>' , { 'class' : 'cp-app-drive-tree-category' } )
. append ( $trashElement ) ;
$container . append ( $trashList ) ;
} ;
var categories = { } ;
categories [ FILES _DATA ] = {
name : FILES _DATA _NAME ,
$icon : $unsortedIcon
} ;
categories [ TEMPLATE ] = {
name : TEMPLATE _NAME ,
droppable : true ,
$icon : $templateIcon
} ;
categories [ RECENT ] = {
name : RECENT _NAME ,
$icon : $recentIcon
} ;
categories [ OWNED ] = {
name : OWNED _NAME ,
$icon : $ownedIcon
} ;
categories [ SEARCH ] = {
name : Messages . fm _searchPlaceholder ,
$icon : $searchIcon
} ;
categories [ TAGS ] = {
name : TAGS _NAME ,
$icon : $tagsIcon
} ;
var createCategory = function ( $container , cat ) {
var options = categories [ cat ] ;
var $icon = options . $icon . clone ( ) ;
var isOpened = manager . comparePath ( [ cat ] , currentPath ) ;
var $element = createTreeElement ( options . name , $icon , [ cat ] , options . draggable , options . droppable , false , isOpened ) ;
$element . addClass ( 'cp-app-drive-tree-root' ) ;
var $list = $ ( '<ul>' , { 'class' : 'cp-app-drive-tree-category' } ) . append ( $element ) ;
$container . append ( $list ) ;
} ;
APP . resetTree = function ( ) {
var $categories = $tree . find ( '.cp-app-drive-tree-categories-container' ) ;
var s = $categories . scrollTop ( ) || 0 ;
$tree . html ( '' ) ;
/ *
$ ( h ( 'button.fa.fa-times.cp-close-button' , {
title : Messages . filePicker _close
} ) ) . click ( function ( e ) {
e . stopPropagation ( ) ;
$tree . hide ( ) ;
checkCollapseButton ( ) ;
} ) . appendTo ( $tree ) ;
* /
var $div = $ ( '<div>' , { 'class' : 'cp-app-drive-tree-categories-container' } )
. appendTo ( $tree ) ;
if ( displayedCategories . indexOf ( SEARCH ) !== - 1 ) { createCategory ( $div , SEARCH ) ; }
if ( displayedCategories . indexOf ( TAGS ) !== - 1 ) { createCategory ( $div , TAGS ) ; }
if ( displayedCategories . indexOf ( RECENT ) !== - 1 ) { createCategory ( $div , RECENT ) ; }
if ( displayedCategories . indexOf ( OWNED ) !== - 1 ) { createCategory ( $div , OWNED ) ; }
if ( displayedCategories . indexOf ( ROOT ) !== - 1 ) { createTree ( $div , [ ROOT ] ) ; }
if ( displayedCategories . indexOf ( TEMPLATE ) !== - 1 ) { createCategory ( $div , TEMPLATE ) ; }
if ( displayedCategories . indexOf ( FILES _DATA ) !== - 1 ) { createCategory ( $div , FILES _DATA ) ; }
if ( displayedCategories . indexOf ( TRASH ) !== - 1 ) { createTrash ( $div , [ TRASH ] ) ; }
$tree . append ( APP . $limit ) ;
$categories = $tree . find ( '.cp-app-drive-tree-categories-container' ) ;
$categories . scrollTop ( s ) ;
} ;
APP . hideMenu = function ( e ) {
$contextMenu . hide ( ) ;
$trashTreeContextMenu . hide ( ) ;
$trashContextMenu . hide ( ) ;
$contentContextMenu . hide ( ) ;
$defaultContextMenu . hide ( ) ;
if ( ! e || ! $ ( e . target ) . parents ( '.cp-dropdown' ) ) {
$ ( '.cp-dropdown-content' ) . hide ( ) ;
}
} ;
var stringifyPath = function ( path ) {
if ( ! Array . isArray ( path ) ) { return ; }
var $div = $ ( '<div>' ) ;
var i = 0 ;
var space = 10 ;
path . forEach ( function ( s ) {
if ( i === 0 ) { s = getPrettyName ( s ) ; }
$div . append ( $ ( '<span>' , { 'style' : 'margin: 0 0 0 ' + i * space + 'px;' } ) . text ( s ) ) ;
$div . append ( $ ( '<br>' ) ) ;
i ++ ;
} ) ;
return $div . html ( ) ;
} ;
// Disable middle click in the context menu to avoid opening /drive/inner.html# in new tabs
var onWindowClick = function ( e ) {
if ( ! e . target || ! $ ( e . target ) . parents ( '.cp-dropdown-content' ) . length ) { return ; }
if ( e . which !== 1 ) {
e . stopPropagation ( ) ;
return false ;
}
} ;
APP . getProperties = function ( el , cb ) {
if ( ! manager . isFile ( el ) && ! manager . isSharedFolder ( el ) ) {
return void cb ( 'NOT_FILE' ) ;
}
//var ro = manager.isReadOnlyFile(el);
var data ;
if ( manager . isSharedFolder ( el ) ) {
data = JSON . parse ( JSON . stringify ( manager . getSharedFolderData ( el ) ) ) ;
} else {
data = JSON . parse ( JSON . stringify ( manager . getFileData ( el ) ) ) ;
}
if ( ! data || ! ( data . href || data . roHref ) ) { return void cb ( 'INVALID_FILE' ) ; }
var opts = { } ;
opts . href = Hash . getRelativeHref ( data . href || data . roHref ) ;
opts . channel = data . channel ;
if ( manager . isSharedFolder ( el ) ) {
var ro = folders [ el ] && folders [ el ] . version >= 2 ;
if ( ! ro ) { opts . noReadOnly = true ; }
}
Properties . getPropertiesModal ( common , opts , cb ) ;
} ;
APP . getAccess = function ( el , cb ) {
if ( ! manager . isFile ( el ) && ! manager . isSharedFolder ( el ) ) {
return void cb ( 'NOT_FILE' ) ;
}
var data ;
if ( manager . isSharedFolder ( el ) ) {
data = JSON . parse ( JSON . stringify ( manager . getSharedFolderData ( el ) ) ) ;
} else {
data = JSON . parse ( JSON . stringify ( manager . getFileData ( el ) ) ) ;
}
if ( ! data || ! ( data . href || data . roHref ) ) { return void cb ( 'INVALID_FILE' ) ; }
var opts = { } ;
opts . href = Hash . getRelativeHref ( data . href || data . roHref ) ;
opts . channel = data . channel ;
// Transfer ownership: templates are stored as templates for other users/teams
if ( currentPath [ 0 ] === TEMPLATE ) {
opts . isTemplate = true ;
}
// Shared folders: no expiration date
if ( manager . isSharedFolder ( el ) ) {
opts . noExpiration = true ;
}
Access . getAccessModal ( common , opts , cb ) ;
} ;
if ( ! APP . loggedIn ) {
$contextMenu . find ( '.cp-app-drive-context-delete' ) . attr ( 'data-icon' , faDelete )
. html ( $contextMenu . find ( '.cp-app-drive-context-remove' ) . html ( ) ) ;
}
var deleteOwnedPaths = function ( paths , pathsList ) {
pathsList = pathsList || [ ] ;
if ( paths ) {
paths . forEach ( function ( p ) { pathsList . push ( p . path ) ; } ) ;
}
var msgD = pathsList . length === 1 ? Messages . fm _deleteOwnedPad :
Messages . fm _deleteOwnedPads ;
UI . confirm ( msgD , function ( res ) {
$ ( window ) . focus ( ) ;
if ( ! res ) { return ; }
manager . deleteOwned ( pathsList , function ( ) {
pathsList . forEach ( LS . removeFoldersOpened ) ;
removeSelected ( ) ;
refresh ( ) ;
} ) ;
} ) ;
} ;
var downloadFolder = function ( folderElement , folderName , sfId ) {
var todo = function ( data ) {
data . folder = folderElement ;
data . sharedFolderId = sfId ;
data . name = Util . fixFileName ( folderName ) ;
data . folderName = Util . fixFileName ( folderName ) + '.zip' ;
var uo = manager . user . userObject ;
if ( sfId && manager . folders [ sfId ] ) {
uo = manager . folders [ sfId ] . userObject ;
}
if ( uo . getFilesRecursively ) {
data . list = uo . getFilesRecursively ( folderElement ) . map ( function ( el ) {
var d = uo . getFileData ( el ) ;
return d . channel ;
} ) ;
}
APP . FM . downloadFolder ( data , function ( err , obj ) {
console . log ( err , obj ) ;
console . log ( 'DONE' ) ;
} ) ;
} ;
todo ( {
uo : proxy ,
sf : folders ,
} ) ;
} ;
$contextMenu . on ( "click" , "a" , function ( e ) {
e . stopPropagation ( ) ;
var paths = $contextMenu . data ( 'paths' ) ;
var pathsList = [ ] ;
var type = $contextMenu . attr ( 'data-menu-type' ) ;
var $this = $ ( this ) ;
var el , data ;
if ( paths . length === 0 ) {
log ( Messages . fm _forbidden ) ;
debug ( "Context menu on a forbidden or unexisting element. " , paths ) ;
return ;
}
if ( $this . hasClass ( "cp-app-drive-context-rename" ) ) {
if ( paths . length !== 1 ) { return ; }
displayRenameInput ( paths [ 0 ] . element , paths [ 0 ] . path ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-color" ) ) {
var currentColor = getFolderColor ( paths [ 0 ] . path ) ;
pickFolderColor ( paths [ 0 ] . element , currentColor , function ( color ) {
paths . forEach ( function ( p ) {
setFolderColor ( p . element , p . path , color ) ;
} ) ;
refresh ( ) ;
} ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-delete" ) ) {
if ( ! APP . loggedIn ) {
return void deletePaths ( paths ) ;
}
paths . forEach ( function ( p ) { pathsList . push ( p . path ) ; } ) ;
moveElements ( pathsList , [ TRASH ] , false , refresh ) ;
}
else if ( $this . hasClass ( 'cp-app-drive-context-deleteowned' ) ) {
deleteOwnedPaths ( paths ) ;
}
else if ( $this . hasClass ( 'cp-app-drive-context-preview' ) ) {
if ( paths . length !== 1 ) { return ; }
el = manager . find ( paths [ 0 ] . path ) ;
openFile ( el , null , false ) ;
}
else if ( $this . hasClass ( 'cp-app-drive-context-open' ) ) {
paths . forEach ( function ( p ) {
var el = manager . find ( p . path ) ;
if ( files . restrictedFolders [ el ] ) {
UI . warn ( Messages . fm _restricted ) ;
return ;
}
openFile ( el , false , true ) ;
} ) ;
}
else if ( $this . hasClass ( 'cp-app-drive-context-openro' ) ) {
paths . forEach ( function ( p ) {
var el = manager . find ( p . path ) ;
if ( paths [ 0 ] . path [ 0 ] === SHARED _FOLDER && APP . newSharedFolder ) {
// ANON_SHARED_FOLDER
el = manager . find ( paths [ 0 ] . path . slice ( 1 ) , APP . newSharedFolder ) ;
}
if ( manager . isPathIn ( p . path , [ FILES _DATA ] ) ) {
el = p . path [ 1 ] ;
} else {
if ( ! el || manager . isFolder ( el ) ) { return ; }
}
openFile ( el , true , true ) ;
} ) ;
}
else if ( $this . hasClass ( 'cp-app-drive-context-makeacopy' ) ) {
if ( paths . length !== 1 ) { return ; }
el = manager . find ( paths [ 0 ] . path ) ;
( function ( ) {
var path = currentPath ;
if ( path [ 0 ] !== ROOT ) { path = [ ROOT ] ; }
var _metadata = manager . getFileData ( el ) ;
var _simpleData = {
title : _metadata . filename || _metadata . title ,
href : _metadata . href || _metadata . roHref ,
password : _metadata . password ,
channel : _metadata . channel ,
} ;
var parsed = Hash . parsePadUrl ( _metadata . href || _metadata . roHref ) ;
openIn ( parsed . type , path , APP . team , _simpleData ) ;
} ) ( ) ;
}
else if ( $this . hasClass ( 'cp-app-drive-context-openincode' ) ) {
if ( paths . length !== 1 ) { return ; }
var p = paths [ 0 ] ;
el = manager . find ( p . path ) ;
( function ( ) {
var path = currentPath ;
if ( path [ 0 ] !== ROOT ) { path = [ ROOT ] ; }
var _metadata = manager . getFileData ( el ) ;
var _simpleData = {
title : _metadata . filename || _metadata . title ,
href : _metadata . href || _metadata . roHref ,
password : _metadata . password ,
channel : _metadata . channel ,
} ;
openIn ( 'code' , path , APP . team , _simpleData ) ;
} ) ( ) ;
}
else if ( $this . hasClass ( 'cp-app-drive-context-expandall' ) ||
$this . hasClass ( 'cp-app-drive-context-collapseall' ) ) {
if ( paths . length !== 1 ) { return ; }
var opened = $this . hasClass ( 'cp-app-drive-context-expandall' ) ;
var openRecursive = function ( path ) {
LS . setFolderOpened ( path , opened ) ;
var folderContent = manager . find ( path ) ;
var subfolders = [ ] ;
for ( var k in folderContent ) {
if ( manager . isFolder ( folderContent [ k ] ) ) {
if ( manager . isSharedFolder ( folderContent [ k ] ) ) {
subfolders . push ( [ k ] . concat ( manager . user . userObject . ROOT ) ) ;
}
else {
subfolders . push ( k ) ;
}
}
}
subfolders . forEach ( function ( p ) {
var subPath = path . concat ( p ) ;
openRecursive ( subPath ) ;
} ) ;
} ;
openRecursive ( paths [ 0 ] . path ) ;
refresh ( ) ;
}
else if ( $this . hasClass ( 'cp-app-drive-context-download' ) ) {
if ( paths . length !== 1 ) { return ; }
var path = paths [ 0 ] ;
el = manager . find ( path . path ) ;
// folder
if ( manager . isFolder ( el ) ) {
// folder
var name , folderEl ;
if ( ! manager . isSharedFolder ( el ) ) {
name = path . path [ path . path . length - 1 ] ;
folderEl = el ;
var sfId = manager . isInSharedFolder ( path . path ) ;
downloadFolder ( folderEl , name , sfId ) ;
}
// shared folder
else {
data = manager . getSharedFolderData ( el ) ;
name = data . title ;
folderEl = manager . find ( path . path . concat ( "root" ) ) ;
downloadFolder ( folderEl , name , el ) ;
}
}
// file
else if ( manager . isFile ( el ) ) {
// imported file
if ( path . element . is ( ".cp-border-color-file" ) ) {
data = manager . getFileData ( el ) ;
APP . FM . downloadFile ( data , function ( err , obj ) {
console . log ( err , obj ) ;
console . log ( 'DONE' ) ;
} ) ;
}
// pad
else {
data = manager . getFileData ( el ) ;
APP . FM . downloadPad ( data , function ( err , obj ) {
console . log ( err , obj ) ;
console . log ( 'DONE' ) ;
} ) ;
}
}
}
else if ( $this . hasClass ( 'cp-app-drive-context-share' ) ) {
if ( paths . length !== 1 ) { return ; }
el = manager . find ( paths [ 0 ] . path ) ;
var parsed ;
var friends = common . getFriends ( ) ;
var anonDrive = manager . isPathIn ( currentPath , [ FILES _DATA ] ) && ! APP . loggedIn ;
if ( manager . isFolder ( el ) && ! manager . isSharedFolder ( el ) && ! anonDrive ) { // Folder
// if folder is inside SF
if ( manager . isInSharedFolder ( paths [ 0 ] . path ) ) {
return void UI . alert ( Messages . convertFolderToSF _SFParent ) ;
}
// if folder already contains SF
else if ( manager . hasSubSharedFolder ( el ) ) {
return void UI . alert ( Messages . convertFolderToSF _SFChildren ) ;
}
// if root
else if ( paths [ 0 ] . path . length <= 1 ) {
return void UI . warn ( Messages . error ) ;
}
// if folder does not contains SF
// XXX rewrite "creation_owned1": "An <b>owned</b> pad can be deleted from the server whenever the owner wants. Deleting an owned pad removes it from other users' CryptDrives."
// tipy used in dialog to create a shared folder
else {
var convertContent = h ( 'div' , [
h ( 'p' , Messages . convertFolderToSF _confirm ) ,
h ( 'label' , { for : 'cp-upload-password' } , Messages . fm _shareFolderPassword ) ,
UI . passwordInput ( {
id : 'cp-upload-password' ,
placeholder : Messages . creation _passwordValue
} ) ,
h ( 'span' , {
style : 'display:flex;align-items:center;justify-content:space-between'
} , [
UI . createCheckbox ( 'cp-upload-owned' , Messages . sharedFolders _create _owned , true ) ,
UI . createHelper ( 'https://docs.cryptpad.fr/en/user_guide/share_and_access.html#owners' , Messages . creation _owned1 )
] ) ,
] ) ;
return void UI . confirm ( convertContent , function ( res ) {
if ( ! res ) { return ; }
var password = $ ( convertContent ) . find ( '#cp-upload-password' ) . val ( ) || undefined ;
var owned = Util . isChecked ( $ ( convertContent ) . find ( '#cp-upload-owned' ) ) ;
manager . convertFolderToSharedFolder ( paths [ 0 ] . path , owned , password , function ( err , obj ) {
if ( err || obj && obj . error ) { return void console . error ( err || obj . error ) ; }
if ( obj && obj . fId ) { APP . convertedFolder = obj . fId ; }
refresh ( ) ;
} ) ;
} ) ;
}
} else { // File or shared folder
var sf = ! anonDrive && manager . isSharedFolder ( el ) ;
if ( anonDrive ) {
data = el ;
} else {
data = sf ? manager . getSharedFolderData ( el ) : manager . getFileData ( el ) ;
}
parsed = ( data . href && data . href . indexOf ( '#' ) !== - 1 ) ? Hash . parsePadUrl ( data . href ) : { } ;
var roParsed = Hash . parsePadUrl ( data . roHref ) ;
var padType = parsed . type || roParsed . type ;
var ro = ! sf || ( folders [ el ] && folders [ el ] . version >= 2 ) ;
var padData = {
teamId : APP . team ,
origin : APP . origin ,
pathname : "/" + padType + "/" ,
friends : friends ,
password : data . password ,
hashes : {
editHash : parsed . hash ,
viewHash : ro && roParsed . hash ,
fileHash : parsed . hash
} ,
fileData : {
hash : parsed . hash ,
password : data . password
} ,
isTemplate : paths [ 0 ] . path [ 0 ] === 'template' ,
title : data . title ,
sharedFolder : sf ,
common : common
} ;
if ( padType === 'file' ) {
return void Share . getFileShareModal ( common , padData ) ;
}
Share . getShareModal ( common , padData ) ;
}
}
else if ( $this . hasClass ( 'cp-app-drive-context-savelocal' ) ) {
if ( paths . length !== 1 ) { return ; }
el = manager . find ( paths [ 0 ] . path ) ;
if ( manager . isFile ( el ) ) {
data = manager . getFileData ( el ) ;
} else if ( manager . isSharedFolder ( el ) ) {
data = manager . getSharedFolderData ( el ) ;
}
if ( ! data ) { return ; }
sframeChan . query ( 'Q_STORE_IN_TEAM' , {
href : data . href || data . rohref ,
password : data . password ,
path : paths [ 0 ] . path [ 0 ] === 'template' ? [ 'template' ] : undefined ,
title : data . title || '' ,
teamId : - 1
} , function ( err ) {
if ( err ) { return void console . error ( err ) ; }
} ) ;
}
else if ( $this . hasClass ( 'cp-app-drive-context-newfolder' ) ) {
if ( paths . length !== 1 ) { return ; }
var onFolderCreated = function ( err , info ) {
if ( err ) { return void logError ( err ) ; }
APP . newFolder = info . newPath ;
APP . displayDirectory ( paths [ 0 ] . path ) ;
} ;
el = manager . find ( paths [ 0 ] . path ) ;
if ( manager . isSharedFolder ( el ) ) {
paths [ 0 ] . path . push ( ROOT ) ;
}
manager . addFolder ( paths [ 0 ] . path , null , onFolderCreated ) ;
}
else if ( $this . hasClass ( 'cp-app-drive-context-newsharedfolder' ) ) {
if ( paths . length !== 1 ) { return ; }
addSharedFolderModal ( function ( obj ) {
if ( ! obj ) { return ; }
manager . addSharedFolder ( paths [ 0 ] . path , obj , refresh ) ;
} ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-uploadfiles" ) ) {
showUploadFilesModal ( ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-uploadfolder" ) ) {
showUploadFolderModal ( ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-newdoc" ) ) {
var ntype = $this . data ( 'type' ) || 'pad' ;
var path2 = manager . isPathIn ( currentPath , [ TRASH ] ) ? '' : currentPath ;
openIn ( ntype , path2 , APP . team ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-properties" ) ) {
if ( type === 'trash' ) {
var pPath = paths [ 0 ] . path ;
if ( paths . length !== 1 || pPath . length !== 4 ) { return ; }
var element = manager . find ( pPath . slice ( 0 , 3 ) ) ; // element containing the oldpath
var sPath = stringifyPath ( element . path ) ;
UI . alert ( '<strong>' + Messages . fm _originalPath + "</strong>:<br>" + sPath , undefined , true ) ;
return ;
}
if ( paths . length !== 1 ) { return ; }
el = manager . find ( paths [ 0 ] . path ) ;
if ( paths [ 0 ] . path [ 0 ] === SHARED _FOLDER && APP . newSharedFolder ) {
// ANON_SHARED_FOLDER
el = manager . find ( paths [ 0 ] . path . slice ( 1 ) , APP . newSharedFolder ) ;
}
APP . getProperties ( el , function ( e ) {
if ( e ) { return void logError ( e ) ; }
} ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-access" ) ) {
if ( paths . length !== 1 ) { return ; }
el = manager . find ( paths [ 0 ] . path ) ;
if ( paths [ 0 ] . path [ 0 ] === SHARED _FOLDER && APP . newSharedFolder ) {
// ANON_SHARED_FOLDER
el = manager . find ( paths [ 0 ] . path . slice ( 1 ) , APP . newSharedFolder ) ;
}
APP . getAccess ( el , function ( e ) {
if ( e ) { return void logError ( e ) ; }
} ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-hashtag" ) ) {
var hrefs = paths . map ( function ( p ) {
var el = manager . find ( p . path ) ;
var data = manager . getFileData ( el ) ;
return data . href || data . roHref ;
} ) . filter ( Boolean ) ;
common . updateTags ( hrefs ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-empty" ) ) {
if ( paths . length !== 1 || ! paths [ 0 ] . element
|| ! manager . comparePath ( paths [ 0 ] . path , [ TRASH ] ) ) {
log ( Messages . fm _forbidden ) ;
return ;
}
emptyTrashModal ( ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-remove" ) ) {
return void deletePaths ( paths ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-removesf" ) ) {
return void deletePaths ( paths ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-restore" ) ) {
if ( paths . length !== 1 ) { return ; }
var restorePath = paths [ 0 ] . path ;
var restoreName = paths [ 0 ] . path [ paths [ 0 ] . path . length - 1 ] ;
if ( restorePath . length === 4 ) {
var rEl = manager . find ( restorePath ) ;
if ( manager . isFile ( rEl ) ) {
restoreName = manager . getTitle ( rEl ) ;
} else if ( manager . isSharedFolder ( rEl ) ) {
var sfData = manager . getSharedFolderData ( rEl ) ;
restoreName = sfData . title || sfData . lastTitle || Messages . fm _deletedFolder ;
} else {
restoreName = restorePath [ 1 ] ;
}
}
UI . confirm ( Messages . _getKey ( "fm_restoreDialog" , [ restoreName ] ) , function ( res ) {
if ( ! res ) { return ; }
manager . restore ( restorePath , refresh ) ;
} ) ;
}
else if ( $this . hasClass ( "cp-app-drive-context-openparent" ) ) {
if ( paths . length !== 1 ) { return ; }
var parentPath = paths [ 0 ] . path . slice ( ) ;
if ( manager . isInTrashRoot ( parentPath ) ) { parentPath = [ TRASH ] ; }
else { parentPath . pop ( ) ; }
APP . displayDirectory ( parentPath ) ;
APP . selectedFiles = paths [ 0 ] . path . slice ( - 1 ) ;
}
APP . hideMenu ( ) ;
} ) ;
// Chrome considers the double-click means "select all" in the window
$content . on ( 'mousedown' , function ( e ) {
$content . focus ( ) ;
e . preventDefault ( ) ;
} ) ;
$appContainer . on ( 'mouseup' , function ( e ) {
if ( e . which !== 1 ) { return ; }
if ( $ ( e . target ) . is ( ".dropdown-submenu a, .dropdown-submenu a span" ) ) { return ; } // if we click on dropdown-submenu, don't close menu
APP . hideMenu ( e ) ;
} ) ;
$appContainer . on ( 'click' , function ( e ) {
if ( e . which !== 1 ) { return ; }
removeInput ( ) ;
} ) ;
$appContainer . on ( 'drag drop' , function ( e ) {
removeInput ( ) ;
APP . hideMenu ( e ) ;
} ) ;
$appContainer . on ( 'mouseup drop' , function ( ) {
$ ( '.cp-app-drive-element-droppable' ) . removeClass ( 'cp-app-drive-element-droppable' ) ;
} ) ;
$appContainer . on ( 'keydown' , function ( e ) {
// "Del"
if ( e . which === 46 ) {
if ( manager . isPathIn ( currentPath , [ FILES _DATA ] ) && APP . loggedIn ) {
return ; // We can't remove elements directly from filesData
}
var $selected = findSelectedElements ( ) ;
if ( ! $selected . length ) { return ; }
var paths = [ ] ;
var isTrash = manager . isPathIn ( currentPath , [ TRASH ] ) ;
$selected . each ( function ( idx , elmt ) {
if ( ! $ ( elmt ) . data ( 'path' ) ) { return ; }
paths . push ( $ ( elmt ) . data ( 'path' ) ) ;
} ) ;
if ( ! paths . length ) { return ; }
// If we are in the trash or anon USER or if we are holding the "shift" key,
// delete permanently
// Or if we are in a shared folder
// Or if the selection is only shared folders
if ( ! APP . loggedIn || isTrash || manager . isInSharedFolder ( currentPath )
|| e . shiftKey ) {
deletePaths ( null , paths ) ;
return ;
}
// else move to trash
moveElements ( paths , [ TRASH ] , false , refresh ) ;
return ;
}
} ) ;
$appContainer . contextmenu ( function ( ) {
APP . hideMenu ( ) ;
return false ;
} ) ;
var onRefresh = {
refresh : function ( ) {
if ( onRefresh . to ) {
window . clearTimeout ( onRefresh . to ) ;
}
onRefresh . to = window . setTimeout ( refresh , 500 ) ;
}
} ;
var onEvDriveChange = sframeChan . on ( 'EV_DRIVE_CHANGE' , function ( data ) {
if ( history . isHistoryMode ) { return ; }
var path = data . path . slice ( ) ;
var originalPath = data . path . slice ( ) ;
if ( ! APP . loggedIn && APP . newSharedFolder && data . id === APP . newSharedFolder ) {
// ANON_SHARED_FOLDER
return void onRefresh . refresh ( ) ;
}
// Fix the path if this is about a shared folder
if ( data . id && manager . folders [ data . id ] ) {
var uoPath = manager . getUserObjectPath ( manager . folders [ data . id ] . userObject ) ;
if ( uoPath ) {
Array . prototype . unshift . apply ( path , uoPath ) ;
path . unshift ( 'drive' ) ;
}
}
if ( path [ 0 ] !== 'drive' ) { return false ; }
path = path . slice ( 1 ) ;
if ( originalPath [ 0 ] === 'drive' ) { originalPath = originalPath . slice ( 1 ) ; }
var cPath = currentPath . slice ( ) ;
if ( originalPath . length && originalPath [ 0 ] === FILES _DATA ) {
onRefresh . refresh ( ) ;
} else if ( ( manager . isPathIn ( cPath , [ 'hrefArray' , TRASH ] ) && cPath [ 0 ] === path [ 0 ] ) ||
( path . length >= cPath . length && manager . isSubpath ( path , cPath ) ) ) {
// Reload after a few ms to make sure all the change events have been received
onRefresh . refresh ( ) ;
} else {
APP . resetTree ( ) ;
}
return false ;
} ) ;
var onEvDriveRemove = sframeChan . on ( 'EV_DRIVE_REMOVE' , function ( data ) {
if ( history . isHistoryMode ) { return ; }
var path = data . path . slice ( ) ;
if ( ! APP . loggedIn && APP . newSharedFolder && data . id === APP . newSharedFolder ) {
// ANON_SHARED_FOLDER
return void onRefresh . refresh ( ) ;
}
// Fix the path if this is about a shared folder
if ( data . id && manager . folders [ data . id ] ) {
var uoPath = manager . getUserObjectPath ( manager . folders [ data . id ] . userObject ) ;
if ( uoPath ) {
Array . prototype . unshift . apply ( path , uoPath ) ;
path . unshift ( 'drive' ) ;
}
}
if ( path [ 0 ] !== 'drive' ) { return false ; }
path = path . slice ( 1 ) ;
var cPath = currentPath . slice ( ) ;
if ( ( manager . isPathIn ( cPath , [ 'hrefArray' , TRASH ] ) && cPath [ 0 ] === path [ 0 ] ) ||
( path . length >= cPath . length && manager . isSubpath ( path , cPath ) ) ) {
// Reload after a few to make sure all the change events have been received
onRefresh . refresh ( ) ;
} else {
APP . resetTree ( ) ;
}
return false ;
} ) ;
$ ( window ) . on ( 'mouseup' , onWindowMouseUp ) ;
$ ( window ) . on ( 'keydown' , onWindowKeydown ) ;
$ ( window ) . on ( 'click' , onWindowClick ) ;
var removeWindowListeners = function ( ) {
$ ( window ) . off ( 'mouseup' , onWindowMouseUp ) ;
$ ( window ) . off ( 'keydown' , onWindowKeydown ) ;
$ ( window ) . off ( 'click' , onWindowClick ) ;
try {
onEvDriveChange . stop ( ) ;
onEvDriveRemove . stop ( ) ;
} catch ( e ) { }
} ;
if ( APP . histConfig ) {
APP . histConfig . onOpen = function ( ) {
// If we're in a shared folder history, store its id in memory
// so that we remember that this isn't the drive history if
// we browse through the drive
var sfId = manager . isInSharedFolder ( currentPath ) ;
if ( ! sfId ) {
delete history . sfId ;
delete APP . histConfig . sharedFolder ;
return ;
}
history . sfId = sfId ;
var data = manager . getSharedFolderData ( sfId ) ;
var parsed = Hash . parsePadUrl ( data . href || data . roHref ) ;
APP . histConfig . sharedFolder = {
hash : parsed . hash ,
password : data . password
} ;
} ;
history . onEnterHistory = function ( obj ) {
if ( history . sfId ) {
if ( ! obj || typeof ( obj ) !== "object" || Object . keys ( obj ) . length === 0 ) {
return ;
}
copyObjectValue ( folders [ history . sfId ] , obj ) ;
refresh ( ) ;
return ;
}
history . sfId = false ;
var ok = manager . isValidDrive ( obj . drive ) ;
if ( ! ok ) { return ; }
var restricted = files . restrictedFolders ;
copyObjectValue ( files , obj . drive ) ;
files . restrictedFolders = restricted ;
appStatus . isReady = true ;
refresh ( ) ;
} ;
history . onLeaveHistory = function ( ) {
copyObjectValue ( files , proxy . drive ) ;
refresh ( ) ;
} ;
}
var fmConfig = {
teamId : APP . team ,
noHandlers : true ,
onUploaded : function ( ) {
refresh ( ) ;
} ,
body : $ ( 'body' )
} ;
APP . FM = common . createFileManager ( fmConfig ) ;
refresh ( ) ;
UI . removeLoadingScreen ( ) ;
/ *
if ( ! APP . team ) {
sframeChan . query ( 'Q_DRIVE_GETDELETED' , null , function ( err , data ) {
var ids = manager . findChannels ( data ) ;
var titles = [ ] ;
ids . forEach ( function ( id ) {
var title = manager . getTitle ( id ) ;
titles . push ( title ) ;
var paths = manager . findFile ( id ) ;
manager . delete ( paths , refresh ) ;
} ) ;
if ( ! titles . length ) { return ; }
UI . log ( Messages . _getKey ( 'fm_deletedPads' , [ titles . join ( ', ' ) ] ) ) ;
} ) ;
}
* /
var nt = nThen ;
var passwordModal = function ( fId , data , cb ) {
var content = [ ] ;
var folderName = '<b>' + ( data . lastTitle || Messages . fm _newFolder ) + '</b>' ;
content . push ( UI . setHTML ( h ( 'p' ) , Messages . _getKey ( 'drive_sfPassword' , [ folderName ] ) ) ) ;
var newPassword = UI . passwordInput ( {
id : 'cp-app-prop-change-password' ,
placeholder : Messages . settings _changePasswordNew ,
style : 'flex: 1;'
} ) ;
var passwordOk = h ( 'button.btn.btn-secondary' , Messages . properties _changePasswordButton ) ;
var changePass = h ( 'span.cp-password-container' , [
newPassword ,
passwordOk
] ) ;
content . push ( changePass ) ;
var div = h ( 'div' , content ) ;
var locked = false ;
$ ( passwordOk ) . click ( function ( ) {
if ( locked ) { return ; }
var pw = $ ( newPassword ) . find ( '.cp-password-input' ) . val ( ) ;
locked = true ;
$ ( div ) . find ( '.alert' ) . remove ( ) ;
$ ( passwordOk ) . html ( '' ) . append ( h ( 'span.fa.fa-spinner.fa-spin' , { style : 'margin-left: 0' } ) ) ;
manager . restoreSharedFolder ( fId , pw , function ( err , obj ) {
if ( obj && obj . error ) {
var wrong = h ( 'div.alert.alert-danger' , Messages . drive _sfPasswordError ) ;
$ ( div ) . prepend ( wrong ) ;
$ ( passwordOk ) . text ( Messages . properties _changePasswordButton ) ;
locked = false ;
return ;
}
UI . findCancelButton ( $ ( div ) . closest ( '.alertify' ) ) . click ( ) ;
cb ( ) ;
} ) ;
} ) ;
var buttons = [ {
className : 'primary' ,
name : Messages . forgetButton ,
onClick : function ( ) {
manager . delete ( [ [ 'sharedFoldersTemp' , fId ] ] , function ( ) { } ) ;
} ,
keys : [ ]
} , {
className : 'cancel' ,
name : Messages . later ,
onClick : function ( ) { } ,
keys : [ 27 ]
} ] ;
return UI . dialog . customModal ( div , {
buttons : buttons ,
onClose : cb
} ) ;
} ;
var deprecated = files . sharedFoldersTemp ;
if ( typeof ( deprecated ) === "object" && APP . editable && Object . keys ( deprecated ) . length ) {
Object . keys ( deprecated ) . forEach ( function ( fId ) {
var data = deprecated [ fId ] ;
var sfId = manager . user . userObject . getSFIdFromHref ( data . href ) ;
if ( folders [ fId ] || sfId ) { // This shared folder is already stored in the drive...
return void manager . delete ( [ [ 'sharedFoldersTemp' , fId ] ] , function ( ) { } ) ;
}
nt = nt ( function ( waitFor ) {
UI . openCustomModal ( passwordModal ( fId , data , waitFor ( ) ) ) ;
} ) . nThen ;
} ) ;
nt ( function ( ) {
refresh ( ) ;
} ) ;
}
return {
refresh : refresh ,
close : function ( ) {
APP . closed = true ;
removeWindowListeners ( ) ;
}
} ;
} ;
return {
create : create ,
setEditable : setEditable ,
logError : logError
} ;
} ) ;