mirror of
https://github.com/nextcloud/server.git
synced 2026-03-29 13:53:55 -04:00
Merge pull request #48241 from nextcloud/chore/remove-legacy-files-scripts
This commit is contained in:
commit
74bfca4f09
85 changed files with 13 additions and 20491 deletions
|
|
@ -1,5 +0,0 @@
|
|||
/*!
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/.app-sidebar .detailFileInfoContainer{min-height:50px;padding:15px}.app-sidebar .detailFileInfoContainer>div{clear:both}.app-sidebar .mainFileInfoView .icon{display:inline-block;background-size:16px 16px}.app-sidebar .mainFileInfoView .permalink{padding:6px 10px;vertical-align:top;opacity:.6}.app-sidebar .mainFileInfoView .permalink:hover,.app-sidebar .mainFileInfoView .permalink:focus{opacity:1}.app-sidebar .mainFileInfoView .permalink-field>input{clear:both;width:90%}.app-sidebar .thumbnailContainer.large{margin-inline:-15px -35px;margin-top:-15px}.app-sidebar .thumbnailContainer.large.portrait{margin:0}.app-sidebar .large .thumbnail{width:100%;display:block;background-repeat:no-repeat;background-position:center;background-size:100%;float:none;margin:0;height:auto}.app-sidebar .large .thumbnail .stretcher{content:"";display:block;padding-bottom:56.25%}.app-sidebar .large.portrait .thumbnail{background-position:50% top}.app-sidebar .large.portrait .thumbnail{background-size:contain}.app-sidebar .large.text{overflow-y:scroll;overflow-x:hidden;padding-top:14px;font-size:80%;margin-inline-start:0}.app-sidebar .thumbnail{width:100%;min-height:75px;display:inline-block;float:left;margin-inline-end:10px;background-size:contain;background-repeat:no-repeat}.app-sidebar .ellipsis{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.app-sidebar .fileName{font-size:16px;padding-top:13px;padding-bottom:3px}.app-sidebar .fileName h3{width:calc(100% - 42px);display:inline-block;padding:5px 0;margin:-5px 0}.app-sidebar .file-details{color:var(--color-text-maxcontrast)}.app-sidebar .action-favorite{vertical-align:sub;padding:10px;margin:-10px}.app-sidebar .action-favorite>span{opacity:.7 !important}.app-sidebar .detailList{float:left}.app-sidebar .close{position:absolute;top:0;inset-inline-end:0;opacity:.5;z-index:1;width:44px;height:44px}/*# sourceMappingURL=detailsView.css.map */
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"sourceRoot":"","sources":["detailsView.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA,GAKA,sCACC,gBACA,aAGD,0CACC,WAID,qCACC,qBACA,0BAGD,0CACC,iBACA,mBACA,WAEA,gGAEC,UAGF,sDACC,WACA,UAGD,uCACC,0BACA,iBAGD,gDACC,SAGD,+BACC,WACA,cACA,4BACA,2BACA,qBACA,WACA,SACA,YAGD,0CACC,WACA,cACA,sBAGD,wCACC,4BAGD,wCACC,wBAGD,yBACC,kBACA,kBACA,iBACA,cACA,sBAGD,wBACC,WACA,gBACA,qBACA,WACA,uBACA,wBACA,4BAGD,uBACC,mBACA,uBACA,gBAGD,uBACC,eACA,iBACA,mBAGD,0BACC,wBACA,qBACA,cACA,cAGD,2BACC,oCAGD,8BACC,mBACA,aACA,aAGD,mCACC,sBAGD,yBACC,WAGD,oBACC,kBACA,MACA,mBACA,WACA,UACA,WACA","file":"detailsView.css"}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
/*!
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
.app-sidebar .detailFileInfoContainer {
|
||||
min-height: 50px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.app-sidebar .detailFileInfoContainer > div {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.app-sidebar .mainFileInfoView .icon {
|
||||
display: inline-block;
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
.app-sidebar .mainFileInfoView .permalink {
|
||||
padding: 6px 10px;
|
||||
vertical-align: top;
|
||||
opacity: .6;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.app-sidebar .mainFileInfoView .permalink-field>input {
|
||||
clear: both;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.app-sidebar .thumbnailContainer.large {
|
||||
margin-inline: -15px -35px; /* 15 + 20 for the close button */
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
||||
.app-sidebar .thumbnailContainer.large.portrait {
|
||||
margin: 0; /* if we don't fit the image anyway we give it back the margin */
|
||||
}
|
||||
|
||||
.app-sidebar .large .thumbnail {
|
||||
width:100%;
|
||||
display:block;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 100%;
|
||||
float: none;
|
||||
margin: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.app-sidebar .large .thumbnail .stretcher {
|
||||
content: '';
|
||||
display: block;
|
||||
padding-bottom: 56.25%; /* sets height of .thumbnail to 9/16 of the width */
|
||||
}
|
||||
|
||||
.app-sidebar .large.portrait .thumbnail {
|
||||
background-position: 50% top;
|
||||
}
|
||||
|
||||
.app-sidebar .large.portrait .thumbnail {
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.app-sidebar .large.text {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
padding-top: 14px;
|
||||
font-size: 80%;
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
.app-sidebar .thumbnail {
|
||||
width: 100%;
|
||||
min-height: 75px;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
margin-inline-end: 10px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.app-sidebar .ellipsis {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app-sidebar .fileName {
|
||||
font-size: 16px;
|
||||
padding-top: 13px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.app-sidebar .fileName h3 {
|
||||
width: calc(100% - 42px); /* 36px is the with of the copy link icon, but this breaks so we add some more to be sure */
|
||||
display: inline-block;
|
||||
padding: 5px 0;
|
||||
margin: -5px 0;
|
||||
}
|
||||
|
||||
.app-sidebar .file-details {
|
||||
color: var(--color-text-maxcontrast);
|
||||
}
|
||||
|
||||
.app-sidebar .action-favorite {
|
||||
vertical-align: sub;
|
||||
padding: 10px;
|
||||
margin: -10px;
|
||||
}
|
||||
|
||||
.app-sidebar .action-favorite > span{
|
||||
opacity: .7 !important;
|
||||
}
|
||||
|
||||
.app-sidebar .detailList {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.app-sidebar .close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-end: 0;
|
||||
opacity: .5;
|
||||
z-index: 1;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"sourceRoot":"","sources":["files.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASA,SAEC,YACA,YACA,qBACA,WAED,oEACA,8BACA,kDAEC,+CAED,0BACC,oDAGD,mBACC,kBACA,aACA,SACA,4CACC,sBACA,YACA,SACA,oDACA,8CACA,aACA,iBAIF,gBACC,aAGD,OACC,sBACA,YACA,aACA,aACA,mBAGD,6EAGC,yBACA,gCAID,kBACC,kBACA,WACA,gBACA,cACA,sBAEA,6CACC,aAGD,wBACC,wBACA,gBAEA,SAEA,WACA,cACA,0DAMD,wBACC,cACA,WAEA,mGAEC,8CAEA,6KACC,oCAKF,8DACC,oBAKH,yBACC,aAID,uCACC,cACA,WAGD,wBAGC,yBAEA,qBAGD,6FACC,+DAGD,iCACC,yDAGD,kFACC,0CAGD,4EACC,+DAID,iCACC,WACA,YACA,eACA,SACA,eAGD,wCACC,aAGD,0CACC,WAGD,2BACC,YAED,4KAKC,+CAED,wMAKC,oDAGD,qCAEA,yDACC,oCAED,kCACC,iCACA,8BACA,4BACA,yBACA,mBAED,wGAIC,UACA,oCAGD,oBACC,oCAED,uBACC,6BAED,sBACC,cACA,aACA,YACA,sBACA,2BACA,sBACA,oCACC,kBAGF,kCACC,qBACA,wBAED,2BACC,uBACA,yBAGD,uCACC,sBAGD,yBACC,WACA,WACA,wBACA,qBACA,2BACA,WAED,wJAIC,kBAED,2CACC,eAED,4EAEC,mBAGD,kBAEC,4CACA,iBACA,mBAED,SACC,eACA,kBACA,+BACA,4BAED,qBACC,kBACA,aACA,UAGD,uBACC,kBACA,YAGD,0BACC,gBAED,uCACC,eAED,8EAEC,2BACA,sBACA,kBAEA,gBAGD,qMAQC,gBACA,qPACC,MAIF,2BACC,0DACA,iBAGD,sDACC,sBAGD,+BACC,kBACA,aAED,kCACC,aAGD,0DAGC,WACA,kBAED,kDAEC,aACA,kBACA,2BACA,sBACA,YACA,iBACA,UAED,qCAEC,QACA,eACA,eACA,YAGA,8DACC,WAED,mEACC,WAGF,6BACC,qBACA,WACA,YACA,wBACA,2BACA,4BACA,wBACA,eACA,mCACA,eACA,kBACA,UAED,oCACC,eAID,2CACC,qCAGD,iDACC,qBACA,oCACA,YAED,uBACC,iBACA,kBACA,SAGD,6IACA,8FAEA,wCACC,kBACA,gBACA,uBACA,YAKA,kBACC,YACA,4BACC,QACA,YACA,aACA,gBACA,mBACA,uBACA,YACA,WACA,UACA,wBAKH,iJAEC,wBAGD,mCACC,iCACA,8BACA,4BACA,yBAED,4BACC,WAGD,2CACC,uBACA,gBACA,kBACA,mBAKD,8BACC,kBACA,mBAEA,yBACA,qBACA,SACA,YACA,cAEA,yBACA,eAEA,iBACA,oCACA,uBACA,mBAGD,mBACC,UAID,6DACC,WACA,oBAID,iRAIC,UAID,0EACC,WAMA,wEACC,aAGD,oGACC,+CACA,wCACA,wBACA,yDACA,aAIF,oGAEC,mBAGD,+BACC,kBACA,WACA,iBACA,wJAGD,wFAEC,kBACA,UACA,YAGD,yCACC,qBACA,WAED,8CACC,kBACA,cACA,SACA,sBACA,iBACA,kBACA,wDAEC,8CACA,8CACA,oBAEA,WACA,YACA,aACA,qBACA,uBAGF,8DACC,+CAGD,iDAGA,aACC,WAGD,iCACC,kBAID,mDAEC,gBAID,oCACC,qBACA,0BAGD,8EACC,0BAOA,kCACC,eAGD,sEACC,eAGD,sCACC,gBAIF,aACC,YACA,WACA,2BAKA,0EACC,wCAKF,iBACI,kBACA,qBACA,sBAEJ,wBACI,aAEJ,mBACC,eACA,iBACA,iBAGD,0BACC,aAED,uBACC,kBACA,2BACA,mBAGD,8CACC,gBAIA,8BACC,eACA,iBACA,iBACA,WACA,2CACC,kBACA,0FAGC,kBACA,4BACA,SACA,UACA,WACA,gBAED,mDACC,qBACA,sBAGF,0CACC,iBACA,oBACA,oBAGA,oGACC,WAID,qIAEC,WAED,uDACC,WACA,0HACC,WAIH,wEACC,UAED,oCACC,+CACA,wCAGF,uGACC,WAED,wDACC,UAKF,4EACC,qBACA,eACA,gBACA,uBACA,sBACA,sBAGD,2CACC,iCAGD,yCACC,UAGD,kNAKC,UAGD,qCACC,gBAGD,0FAEC,WAGD,mDACC,eAGD,SACC,oCAGA,aAED,wCACC,WAEA,2BAKD,sBACC,aAED,2DAIC,+BAED,YACC,mBACA,mBACA,iBAED,wBACC,UAED,YACC,qBAGD,iBACC,WACA,aAED,6BACC,yBACA,YAGA,gBAED,yBACC,uBAED,MACC,WACA,kBACA,QACA,8CACA,sCACA,wBACA,WACA,yBACA,8BACA,4BACA,6BACA,iCAED,kBACC,UAGD,aACC,gBACA,SACA,oCACA,eACA,gBACA,aAGA,oBACC,mCAKF,gBACC,sBACA,wBACA,gBACA,YACA,UACA,SACA,0DACA,WACA,yBACA,sBACA,qBACA,iBACA,aACA,MACA,0BAKE,0IACC,sBACA,qBACA,aACA,YACA,WACA,YACA,mBACA,uBAED,oFACC,aAQJ,0DACC,qBAGD,6KAIC,qBACA,sBACA,0BAMA,sDACC,sBAED,yDACC,uDAIF,iJAGC,aAGD,oJAGC,WACA,YAGD,gCACC,kBACA,YACA,SACA,oDACA,8CACA,aACA,iBAGD,YACC,mBAEA,uBACC,mCAIF,0DAEC,oCAED,qBACC,oCACA,4BACC,2BAIF,cACC,wBACA,gBACA,6BACA,cACA,gBACA,YAEA,2BACC,aAGD,kCACC,UACA,kBACA,sBAIF,uBACC,oBACA,YACA,gBACA,+BACA,UACA,YACA,wBACA,sBAEA,6BACC,YAKA,oEACC,0BAIF,kCACC,WACA,mCAWA,kDACC,cACA,4CACA,0DACA,qDACC,WACA,YAMH,+CACC,aACA,+CACA,6BACA,aACA,cAGA,+DACC,cACA,kBACA,aACA,mCAEA,0fAKC,+BAEA,oxDAGC,+CAKH,kDACC,eACA,mBAGC,8EACC,YACA,eACA,kBACA,MAvDQ,MAwDR,OAxDQ,MAyDR,QAxDO,KAyDP,MACA,qBACA,WAEA,yFACC,0BACA,2BACA,wBACA,SACA,mCACA,4BACA,2BAKA,wGACC,wBACA,UACA,uBAKH,uEACC,WACA,SACA,MACA,YAEA,YACA,gBAEA,0BAGD,iEACC,YACA,mCAIA,gBAKA,0BAEA,2EACC,aACA,YACA,iBACA,kBACA,iBACA,UAEA,0FACC,qBACA,kBACA,gBACA,uBACA,mBAED,kFACC,WACA,OACA,eAED,iFACC,WACA,OACA,eAID,sFACC,aAKF,8EACC,aAGD,8EACC,eACA,iBACA,aACA,mBACA,kBACA,mBAEA,sFACC,QAxJK,KAyJL,WACA,YACA,aACA,mBACA,uBAGA,wGACC,aAQH,2GACC,yBAEA,6HACC,YACA,kBAIF,6GACC,yBAGD,6GACC,yBAIF,gEACC,iBACA,mCAEA,+EACC,WACA,sBACA,YAMH,kHAEC,aAGD,sIAEC,kBACA,SACA,wBACA,aACA,WAEA,kJACC,WACA,YACA,oBACA,QAzNO,KA0NP,kKACC,SACA,MA5NM,KA6NN,OA7NM,KAmOT,+DACC,qBACA,YACA,aAGA,yFACC,gBACA,uBAMJ,+FACC,cAID,+CACC,aAEA,qEACC,qBACA,cAEA,aAEA,wEACC,iBAEA,iKAEC,aAGD,8EACI,sBAQR,aACC,0DACA,YACA,SACA,aACA,WACA,YACA,8CACA,iCACA,YACA,gBAEA,uEAGC,UAGD,oEAEC,mEASF,cACC,eACA,MAOC,uGACC,gBAID,4EACC,yBAKF,0BACC,kBACA,mBACA,MAKF,gBACC,aAGD,8BACC,gBACA,sBACA,kBACA,kBACA,aACA,eACA,mBAEA,iCACC,WACA,eAGD,6DACC,aACA,YACA","file":"files.css"}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-FileCopyrightText: 2012-2016 ownCloud, Inc.
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,2 +0,0 @@
|
|||
SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
/*!
|
||||
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
@import 'files.scss';
|
||||
@import 'upload.scss';
|
||||
@import 'mobile.scss';
|
||||
@import 'detailsView.scss';
|
||||
@import '../../../core/css/whatsnew.scss';
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
/*!
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*//*!
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/@media only screen and (max-width: 988px)and (min-width: 1025px),only screen and (max-width: 688px){.app-files #app-content.dir-drop{background-color:#fff !important}table th.column-size,table td.filesize,table th.column-mtime,table td.date{display:none}table td{padding:0}table.multiselect thead{padding-inline-start:0}.fileList a.action.action-menu img{padding-inline-start:0}.fileList .fileActionsMenu{margin-inline-end:6px}.fileList a.action-share span:not(.icon):not(.avatar){position:absolute;inset-inline-start:-10000px;top:auto;width:1px;height:1px;overflow:hidden}td.filename a.name .system-tags{display:none}#uploadprogressbar,#uploadprogressbar .label.inner{width:50px}#uploadprogressbar .desktop{display:none !important}#uploadprogressbar .mobile{display:block !important}table.dragshadow{z-index:1000}}@media only screen and (max-width: 480px){table th .selectedActions{float:right}table th .selectedActions>a span:not(.icon){display:none}table th .selectedActions a{padding:17px 14px}table.multiselect th .columntitle.name{margin-inline-start:0}}/*# sourceMappingURL=mobile.css.map */
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"sourceRoot":"","sources":["mobile.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAWA,oGAEA,iCACC,iCAGD,2EAIC,aAID,SACC,UAID,wBACC,uBAGD,mCACC,uBAGD,2BACC,sBAID,sDACC,kBACA,4BACA,SACA,UACA,WACA,gBAID,gCACC,aAKD,mDACC,WAGD,4BACC,wBAED,2BACC,yBAID,iBACC,cAID,0CAEC,0BACC,YAED,4CACC,aAID,4BACC,kBAID,uCACC","file":"mobile.css"}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
/*!
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
@use 'variables';
|
||||
|
||||
/* 938 = table min-width(688) + app-navigation width: 250\
|
||||
$breakpoint-mobile +1 = size where app-navigation is hidden +1
|
||||
688 = table min-width */
|
||||
$min-table-width: 688px;
|
||||
@media only screen and (max-width: $min-table-width + variables.$navigation-width) and (min-width: variables.$breakpoint-mobile + 1), only screen and (max-width: $min-table-width) {
|
||||
|
||||
.app-files #app-content.dir-drop{
|
||||
background-color: rgba(255, 255, 255, 1)!important;
|
||||
}
|
||||
|
||||
table th.column-size,
|
||||
table td.filesize,
|
||||
table th.column-mtime,
|
||||
table td.date {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* remove padding to let border bottom fill the whole width*/
|
||||
table td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* remove shift for multiselect bar to account for missing navigation */
|
||||
table.multiselect thead {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
.fileList a.action.action-menu img {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
.fileList .fileActionsMenu {
|
||||
margin-inline-end: 6px;
|
||||
}
|
||||
/* hide text of the share action on mobile */
|
||||
/* .hidden-visually for accessbility */
|
||||
.fileList a.action-share span:not(.icon):not(.avatar) {
|
||||
position: absolute;
|
||||
inset-inline-start: -10000px;
|
||||
top: auto;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// Hide system tags on mobile
|
||||
td.filename a.name .system-tags {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* shorten elements for mobile */
|
||||
#uploadprogressbar, #uploadprogressbar .label.inner {
|
||||
width: 50px;
|
||||
}
|
||||
/* hide desktop-only parts */
|
||||
#uploadprogressbar .desktop {
|
||||
display: none !important;
|
||||
}
|
||||
#uploadprogressbar .mobile {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* ensure that it is visible over #app-content */
|
||||
table.dragshadow {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
}
|
||||
@media only screen and (max-width: 480px) {
|
||||
/* Only show icons */
|
||||
table th .selectedActions {
|
||||
float: right;
|
||||
}
|
||||
table th .selectedActions > a span:not(.icon) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Increase touch area for the icons */
|
||||
table th .selectedActions a {
|
||||
padding: 17px 14px;
|
||||
}
|
||||
|
||||
/* Remove the margin to reduce the overlap between the name and the icons */
|
||||
table.multiselect th .columntitle.name {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
/*!
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2013-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/#upload{box-sizing:border-box;height:36px;width:39px;padding:0 !important;margin-inline-start:3px;overflow:hidden;vertical-align:top;position:relative;z-index:-20}#upload .icon-upload{position:relative;display:block;width:100%;height:44px;width:44px;margin:-5px -3px;cursor:pointer;z-index:10;opacity:.65}.file_upload_target{display:none}.file_upload_form{display:inline;float:left;margin:0;padding:0;cursor:pointer;overflow:visible}.uploadprogresswrapper,.uploadprogresswrapper *{box-sizing:border-box}.uploadprogresswrapper{display:inline-block;vertical-align:top;height:36px;margin-inline-start:3px}.uploadprogresswrapper>input[type=button]{height:36px;margin-inline-start:3px}#uploadprogressbar{border-color:var(--color-border-dark);border-radius:var(--border-radius-pill) 0 0 var(--border-radius-pill);border-inline-end:0;position:relative;float:left;width:200px;height:44px;display:inline-block;text-align:center}#uploadprogressbar .ui-progressbar-value{margin-top:.1em}#uploadprogressbar .ui-progressbar-value.ui-widget-header.ui-corner-left{height:calc(100% + 2px);top:-2px;inset-inline-start:-1px;position:absolute;overflow:hidden;background-color:var(--color-primary-element)}#uploadprogressbar .label{top:8px;opacity:1;overflow:hidden;white-space:nowrap;font-weight:normal}#uploadprogressbar .label.inner{color:var(--color-primary-element-text);position:absolute;display:block;width:200px}#uploadprogressbar .label.outer{position:relative;color:var(--color-main-text)}#uploadprogressbar .desktop{display:block}#uploadprogressbar .mobile{display:none}#uploadprogressbar+.stop{border-start-start-radius:0;border-end-start-radius:0}.oc-dialog .fileexists{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;margin-bottom:30px}.oc-dialog .fileexists .conflict .filename,.oc-dialog .fileexists .conflict .mtime,.oc-dialog .fileexists .conflict .size{-webkit-touch-callout:initial;-webkit-user-select:initial;-khtml-user-select:initial;-moz-user-select:initial;-ms-user-select:initial;user-select:initial}.oc-dialog .fileexists .conflict .message{color:#e9322d}.oc-dialog .fileexists table{width:100%}.oc-dialog .fileexists th{padding-inline:0}.oc-dialog .fileexists th input[type=checkbox]{margin-inline-end:3px}.oc-dialog .fileexists th:first-child{width:225px}.oc-dialog .fileexists th label{font-weight:normal;color:var(--color-main-text)}.oc-dialog .fileexists th .count{margin-inline-start:3px}.oc-dialog .fileexists .conflicts .template{display:none}.oc-dialog .fileexists .conflict{width:100%;height:85px}.oc-dialog .fileexists .conflict .filename{color:#777;word-break:break-all;clear:left}.oc-dialog .fileexists .icon{width:64px;height:64px;margin:0px 5px 5px 5px;background-repeat:no-repeat;background-size:64px 64px;float:left}.oc-dialog .fileexists .original,.oc-dialog .fileexists .replacement{float:left;width:50%}.oc-dialog .fileexists .conflicts{overflow-y:auto;max-height:225px}.oc-dialog .fileexists .conflict input[type=checkbox]{float:left}.oc-dialog .fileexists #allfileslabel{float:right}.oc-dialog .fileexists #allfiles{vertical-align:bottom;position:relative;top:-3px}.oc-dialog .fileexists #allfiles+span{vertical-align:bottom}.oc-dialog .oc-dialog-buttonrow{width:100%;text-align:right}.oc-dialog .oc-dialog-buttonrow .cancel{float:left}.highlightUploaded{-webkit-animation:highlightAnimation 2s 1;-moz-animation:highlightAnimation 2s 1;-o-animation:highlightAnimation 2s 1;animation:highlightAnimation 2s 1}@-webkit-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@-moz-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@-o-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}/*# sourceMappingURL=upload.css.map */
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"sourceRoot":"","sources":["upload.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA,GAKA,QACC,sBACA,YACA,WACA,qBACA,wBACA,gBACA,mBACA,kBACA,YAED,qBACC,kBACA,cACA,WACA,YACA,WACA,iBACA,eACA,WACA,YAED,iCACA,+FAEA,gDACC,sBAGD,uBACC,qBACA,mBACA,YACA,wBAED,0CACC,YACA,wBAED,mBACC,sCACA,sEACA,oBACA,kBACA,WACA,YACA,YACA,qBACA,kBAEA,yCACC,gBAGF,yEACC,wBACA,SACA,wBACA,kBACA,gBACA,8CAED,0BACC,QACA,UACA,gBACA,mBACA,mBAED,gCACC,wCACA,kBACA,cACA,YAED,gCACC,kBACA,6BAED,4BACC,cAED,2BACC,aAGD,yBACC,4BACA,0BAGD,uBACC,2BACA,yBACA,wBACA,sBACA,qBACA,iBACA,mBAGD,0HAGC,8BACA,4BACA,2BACA,yBACA,wBACA,oBAED,0CACC,cAED,6BACC,WAED,0BACC,iBAED,+CACC,sBAED,sCACC,YAED,gCACC,mBACA,6BAED,iCACC,wBAED,4CACC,aAED,iCACC,WACA,YAED,2CACC,WACA,qBACA,WAED,6BACC,WACA,YACA,uBACA,4BACA,0BACA,WAGD,qEAEC,WACA,UAED,kCACC,gBACA,iBAED,sDACC,WAED,sCACC,YAED,iCACC,sBACA,kBACA,SAED,sCACC,sBAGD,gCACC,WACA,iBAEA,wCACC,WAIF,mBACC,0CACA,uCACA,qCACA,kCAGD,sCACE,4BACA,qCAEF,mCACE,4BACA,qCAEF,iCACE,4BACA,qCAEF,8BACE,4BACA","file":"upload.css"}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-FileCopyrightText: 2013-2016 ownCloud, Inc.
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
/*!
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2013-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
#upload {
|
||||
box-sizing: border-box;
|
||||
height: 36px;
|
||||
width: 39px;
|
||||
padding: 0 !important; /* override default control bar button padding */
|
||||
margin-inline-start: 3px;
|
||||
overflow: hidden;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
z-index: -20;
|
||||
}
|
||||
#upload .icon-upload {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
margin: -5px -3px;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
opacity: .65;
|
||||
}
|
||||
.file_upload_target { display:none; }
|
||||
.file_upload_form { display:inline; float:left; margin:0; padding:0; cursor:pointer; overflow:visible; }
|
||||
|
||||
.uploadprogresswrapper, .uploadprogresswrapper * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.uploadprogresswrapper {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
height: 36px;
|
||||
margin-inline-start: 3px;
|
||||
}
|
||||
.uploadprogresswrapper > input[type='button'] {
|
||||
height: 36px;
|
||||
margin-inline-start: 3px;
|
||||
}
|
||||
#uploadprogressbar {
|
||||
border-color: var(--color-border-dark);
|
||||
border-radius: var(--border-radius-pill) 0 0 var(--border-radius-pill);
|
||||
border-inline-end: 0;
|
||||
position:relative;
|
||||
float: left;
|
||||
width: 200px;
|
||||
height: 44px;
|
||||
display:inline-block;
|
||||
text-align: center;
|
||||
|
||||
.ui-progressbar-value {
|
||||
margin-top:.1em;
|
||||
}
|
||||
}
|
||||
#uploadprogressbar .ui-progressbar-value.ui-widget-header.ui-corner-left {
|
||||
height: calc(100% + 2px);
|
||||
top: -2px;
|
||||
inset-inline-start: -1px;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
background-color: var(--color-primary-element);
|
||||
}
|
||||
#uploadprogressbar .label {
|
||||
top: 8px;
|
||||
opacity: 1;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
font-weight: normal;
|
||||
}
|
||||
#uploadprogressbar .label.inner {
|
||||
color: var(--color-primary-element-text);
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 200px;
|
||||
}
|
||||
#uploadprogressbar .label.outer {
|
||||
position: relative;
|
||||
color: var(--color-main-text);
|
||||
}
|
||||
#uploadprogressbar .desktop {
|
||||
display: block;
|
||||
}
|
||||
#uploadprogressbar .mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#uploadprogressbar + .stop {
|
||||
border-start-start-radius: 0;
|
||||
border-end-start-radius: 0;
|
||||
}
|
||||
|
||||
.oc-dialog .fileexists {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.oc-dialog .fileexists .conflict .filename,
|
||||
.oc-dialog .fileexists .conflict .mtime,
|
||||
.oc-dialog .fileexists .conflict .size {
|
||||
-webkit-touch-callout: initial;
|
||||
-webkit-user-select: initial;
|
||||
-khtml-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
-ms-user-select: initial;
|
||||
user-select: initial;
|
||||
}
|
||||
.oc-dialog .fileexists .conflict .message {
|
||||
color: #e9322d;
|
||||
}
|
||||
.oc-dialog .fileexists table {
|
||||
width: 100%;
|
||||
}
|
||||
.oc-dialog .fileexists th {
|
||||
padding-inline: 0;
|
||||
}
|
||||
.oc-dialog .fileexists th input[type='checkbox'] {
|
||||
margin-inline-end: 3px;
|
||||
}
|
||||
.oc-dialog .fileexists th:first-child {
|
||||
width: 225px;
|
||||
}
|
||||
.oc-dialog .fileexists th label {
|
||||
font-weight: normal;
|
||||
color: var(--color-main-text);
|
||||
}
|
||||
.oc-dialog .fileexists th .count {
|
||||
margin-inline-start: 3px;
|
||||
}
|
||||
.oc-dialog .fileexists .conflicts .template {
|
||||
display: none;
|
||||
}
|
||||
.oc-dialog .fileexists .conflict {
|
||||
width: 100%;
|
||||
height: 85px;
|
||||
}
|
||||
.oc-dialog .fileexists .conflict .filename {
|
||||
color:#777;
|
||||
word-break: break-all;
|
||||
clear: left;
|
||||
}
|
||||
.oc-dialog .fileexists .icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: 0px 5px 5px 5px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 64px 64px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.oc-dialog .fileexists .original,
|
||||
.oc-dialog .fileexists .replacement {
|
||||
float: left;
|
||||
width: 50%;
|
||||
}
|
||||
.oc-dialog .fileexists .conflicts {
|
||||
overflow-y: auto;
|
||||
max-height: 225px;
|
||||
}
|
||||
.oc-dialog .fileexists .conflict input[type='checkbox'] {
|
||||
float: left;
|
||||
}
|
||||
.oc-dialog .fileexists #allfileslabel {
|
||||
float:right;
|
||||
}
|
||||
.oc-dialog .fileexists #allfiles {
|
||||
vertical-align: bottom;
|
||||
position: relative;
|
||||
top: -3px;
|
||||
}
|
||||
.oc-dialog .fileexists #allfiles + span{
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.oc-dialog .oc-dialog-buttonrow {
|
||||
width:100%;
|
||||
text-align:right;
|
||||
|
||||
.cancel {
|
||||
float:left;
|
||||
}
|
||||
}
|
||||
|
||||
.highlightUploaded {
|
||||
-webkit-animation: highlightAnimation 2s 1;
|
||||
-moz-animation: highlightAnimation 2s 1;
|
||||
-o-animation: highlightAnimation 2s 1;
|
||||
animation: highlightAnimation 2s 1;
|
||||
}
|
||||
|
||||
@-webkit-keyframes highlightAnimation {
|
||||
0% { background-color: rgba(255, 255, 140, 1); }
|
||||
100% { background-color: rgba(0, 0, 0, 0); }
|
||||
}
|
||||
@-moz-keyframes highlightAnimation {
|
||||
0% { background-color: rgba(255, 255, 140, 1); }
|
||||
100% { background-color: rgba(0, 0, 0, 0); }
|
||||
}
|
||||
@-o-keyframes highlightAnimation {
|
||||
0% { background-color: rgba(255, 255, 140, 1); }
|
||||
100% { background-color: rgba(0, 0, 0, 0); }
|
||||
}
|
||||
@keyframes highlightAnimation {
|
||||
0% { background-color: rgba(255, 255, 140, 1); }
|
||||
100% { background-color: rgba(0, 0, 0, 0); }
|
||||
}
|
||||
|
|
@ -1,398 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/* global dragOptions, folderDropOptions, OC */
|
||||
(function() {
|
||||
|
||||
if (!OCA.Files) {
|
||||
/**
|
||||
* Namespace for the files app
|
||||
* @namespace OCA.Files
|
||||
*/
|
||||
OCA.Files = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @namespace OCA.Files.App
|
||||
*/
|
||||
OCA.Files.App = {
|
||||
/**
|
||||
* Navigation instance
|
||||
*
|
||||
* @member {OCP.Files.Navigation}
|
||||
*/
|
||||
navigation: null,
|
||||
|
||||
/**
|
||||
* File list for the "All files" section.
|
||||
*
|
||||
* @member {OCA.Files.FileList}
|
||||
*/
|
||||
fileList: null,
|
||||
|
||||
currentFileList: null,
|
||||
|
||||
/**
|
||||
* Backbone model for storing files preferences
|
||||
*/
|
||||
_filesConfig: null,
|
||||
|
||||
/**
|
||||
* Initializes the files app
|
||||
*/
|
||||
initialize: function() {
|
||||
this.$showHiddenFiles = $('input#showhiddenfilesToggle');
|
||||
var showHidden = $('#showHiddenFiles').val() === "1";
|
||||
this.$showHiddenFiles.prop('checked', showHidden);
|
||||
|
||||
// Toggle for grid view
|
||||
this.$showGridView = $('input#showgridview');
|
||||
this.$showGridView.on('change', _.bind(this._onGridviewChange, this));
|
||||
|
||||
if ($('#fileNotFound').val() === "1") {
|
||||
OC.Notification.show(t('files', 'File could not be found'), {type: 'error'});
|
||||
}
|
||||
|
||||
this._filesConfig = OCP.InitialState.loadState('files', 'config', {})
|
||||
|
||||
var { fileid, scrollto, openfile } = OC.Util.History.parseUrlQuery();
|
||||
var fileActions = new OCA.Files.FileActions();
|
||||
// default actions
|
||||
fileActions.registerDefaultActions();
|
||||
// regular actions
|
||||
fileActions.merge(OCA.Files.fileActions);
|
||||
|
||||
this._onActionsUpdated = _.bind(this._onActionsUpdated, this);
|
||||
OCA.Files.fileActions.on('setDefault.app-files', this._onActionsUpdated);
|
||||
OCA.Files.fileActions.on('registerAction.app-files', this._onActionsUpdated);
|
||||
|
||||
this.files = OCA.Files.Files;
|
||||
|
||||
// TODO: ideally these should be in a separate class / app (the embedded "all files" app)
|
||||
this.fileList = new OCA.Files.FileList(
|
||||
$('#app-content-files'), {
|
||||
dragOptions: dragOptions,
|
||||
folderDropOptions: folderDropOptions,
|
||||
fileActions: fileActions,
|
||||
allowLegacyActions: true,
|
||||
scrollTo: scrollto,
|
||||
openFile: openfile,
|
||||
filesClient: OC.Files.getClient(),
|
||||
multiSelectMenu: [
|
||||
{
|
||||
name: 'copyMove',
|
||||
displayName: t('files', 'Move or copy'),
|
||||
iconClass: 'icon-external',
|
||||
order: 10,
|
||||
},
|
||||
{
|
||||
name: 'download',
|
||||
displayName: t('files', 'Download'),
|
||||
iconClass: 'icon-download',
|
||||
order: 10,
|
||||
},
|
||||
OCA.Files.FileList.MultiSelectMenuActions.ToggleSelectionModeAction,
|
||||
{
|
||||
name: 'delete',
|
||||
displayName: t('files', 'Delete'),
|
||||
iconClass: 'icon-delete',
|
||||
order: 99,
|
||||
},
|
||||
...(
|
||||
OCA?.SystemTags === undefined ? [] : ([{
|
||||
name: 'tags',
|
||||
displayName: t('files', 'Tags'),
|
||||
iconClass: 'icon-tag',
|
||||
order: 100,
|
||||
}])
|
||||
),
|
||||
],
|
||||
sorting: {
|
||||
mode: $('#defaultFileSorting').val() === 'basename'
|
||||
? 'name'
|
||||
: $('#defaultFileSorting').val(),
|
||||
direction: $('#defaultFileSortingDirection').val()
|
||||
},
|
||||
config: this._filesConfig,
|
||||
enableUpload: true,
|
||||
maxChunkSize: OC.appConfig.files && OC.appConfig.files.max_chunk_size
|
||||
}
|
||||
);
|
||||
this.updateCurrentFileList(this.fileList)
|
||||
this.files.initialize();
|
||||
|
||||
// for backward compatibility, the global FileList will
|
||||
// refer to the one of the "files" view
|
||||
window.FileList = this.fileList;
|
||||
|
||||
OC.Plugins.attach('OCA.Files.App', this);
|
||||
|
||||
this._setupEvents();
|
||||
|
||||
if (sessionStorage.getItem('WhatsNewServerCheck') < (Date.now() - 3600*1000)) {
|
||||
OCP.WhatsNew.query(); // for Nextcloud server
|
||||
sessionStorage.setItem('WhatsNewServerCheck', Date.now());
|
||||
}
|
||||
|
||||
window._nc_event_bus.emit('files:legacy-view:initialized', this);
|
||||
|
||||
this.navigation = OCP.Files.Navigation
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the app
|
||||
*/
|
||||
destroy: function() {
|
||||
this.fileList.destroy();
|
||||
this.fileList = null;
|
||||
this.files = null;
|
||||
OCA.Files.fileActions.off('setDefault.app-files', this._onActionsUpdated);
|
||||
OCA.Files.fileActions.off('registerAction.app-files', this._onActionsUpdated);
|
||||
},
|
||||
|
||||
_onActionsUpdated: function(ev) {
|
||||
// forward new action to the file list
|
||||
if (ev.action) {
|
||||
this.fileList.fileActions.registerAction(ev.action);
|
||||
} else if (ev.defaultAction) {
|
||||
this.fileList.fileActions.setDefault(
|
||||
ev.defaultAction.mime,
|
||||
ev.defaultAction.name
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the currently active file list
|
||||
*
|
||||
* Due to the file list implementations being registered after clicking the
|
||||
* navigation item for the first time, OCA.Files.App is not aware of those until
|
||||
* they have initialized themselves. Therefore the files list needs to call this
|
||||
* method manually
|
||||
*
|
||||
* @param {OCA.Files.FileList} newFileList -
|
||||
*/
|
||||
updateCurrentFileList: function(newFileList) {
|
||||
if (this.currentFileList === newFileList) {
|
||||
return
|
||||
}
|
||||
|
||||
this.currentFileList = newFileList;
|
||||
if (this.currentFileList !== null) {
|
||||
// update grid view to the current value
|
||||
const isGridView = this.$showGridView.is(':checked');
|
||||
this.currentFileList.setGridView(isGridView);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the currently active file list
|
||||
* @return {?OCA.Files.FileList}
|
||||
*/
|
||||
getCurrentFileList: function () {
|
||||
return this.currentFileList;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the container of the currently visible app.
|
||||
*
|
||||
* @return app container
|
||||
*/
|
||||
getCurrentAppContainer: function() {
|
||||
var viewId = this.getActiveView();
|
||||
return $('#app-content-' + viewId);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the currently active view
|
||||
* @param viewId view id
|
||||
*/
|
||||
setActiveView: function(viewId) {
|
||||
// The Navigation API will handle the final event
|
||||
window._nc_event_bus.emit('files:legacy-navigation:changed', { id: viewId })
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the view id of the currently active view
|
||||
* @return view id
|
||||
*/
|
||||
getActiveView: function() {
|
||||
return this.navigation
|
||||
&& this.navigation.active
|
||||
&& this.navigation.active.id;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Backbone.Model}
|
||||
*/
|
||||
getFilesConfig: function() {
|
||||
return this._filesConfig;
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup events based on URL changes
|
||||
*/
|
||||
_setupEvents: function() {
|
||||
OC.Util.History.addOnPopStateHandler(_.bind(this._onPopState, this));
|
||||
|
||||
// detect when app changed their current directory
|
||||
$('#app-content').delegate('>div', 'changeDirectory', _.bind(this._onDirectoryChanged, this));
|
||||
$('#app-content').delegate('>div', 'afterChangeDirectory', _.bind(this._onAfterDirectoryChanged, this));
|
||||
$('#app-content').delegate('>div', 'changeViewerMode', _.bind(this._onChangeViewerMode, this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for when the current navigation item has changed
|
||||
*/
|
||||
_onNavigationChanged: function(view) {
|
||||
var params;
|
||||
if (view && (view.itemId || view.id)) {
|
||||
if (view.id) {
|
||||
params = {
|
||||
view: view.id,
|
||||
dir: '/',
|
||||
}
|
||||
} else {
|
||||
// Legacy handling
|
||||
params = {
|
||||
view: typeof view.view === 'string' && view.view !== '' ? view.view : view.itemId,
|
||||
dir: view.dir ? view.dir : '/'
|
||||
}
|
||||
}
|
||||
this._changeUrl(params.view, params.dir);
|
||||
OCA.Files.Sidebar.close();
|
||||
this.getCurrentAppContainer().trigger(new $.Event('urlChanged', params));
|
||||
window._nc_event_bus.emit('files:navigation:changed')
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for when an app notified that its directory changed
|
||||
*/
|
||||
_onDirectoryChanged: function(e) {
|
||||
if (e.dir && !e.changedThroughUrl) {
|
||||
this._changeUrl(this.getActiveView(), e.dir, e.fileId);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for when an app notified that its directory changed
|
||||
*/
|
||||
_onAfterDirectoryChanged: function(e) {
|
||||
if (e.dir && e.fileId) {
|
||||
this._changeUrl(this.getActiveView(), e.dir, e.fileId);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for when an app notifies that it needs space
|
||||
* for viewer mode.
|
||||
*/
|
||||
_onChangeViewerMode: function(e) {
|
||||
var state = !!e.viewerModeEnabled;
|
||||
if (e.viewerModeEnabled) {
|
||||
OCA.Files.Sidebar.close();
|
||||
}
|
||||
$('#app-navigation').toggleClass('hidden', state);
|
||||
$('.app-files').toggleClass('viewer-mode no-sidebar', state);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for when the URL changed
|
||||
*/
|
||||
_onPopState: function(params) {
|
||||
params = _.extend({
|
||||
dir: '/',
|
||||
view: 'files'
|
||||
}, params);
|
||||
|
||||
var lastId = this.getActiveView();
|
||||
if (!this.navigation.views.find(view => view.id === params.view)) {
|
||||
params.view = 'files';
|
||||
}
|
||||
|
||||
this.setActiveView(params.view, {silent: true});
|
||||
if (lastId !== this.getActiveView()) {
|
||||
this.getCurrentAppContainer().trigger(new $.Event('show', params));
|
||||
window._nc_event_bus.emit('files:navigation:changed')
|
||||
}
|
||||
|
||||
this.getCurrentAppContainer().trigger(new $.Event('urlChanged', params));
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Encode URL params into a string, except for the "dir" attribute
|
||||
* that gets encoded as path where "/" is not encoded
|
||||
*
|
||||
* @param {Object.<string>} params
|
||||
* @return {string} encoded params
|
||||
*/
|
||||
_makeUrlParams: function(params) {
|
||||
var dir = params.dir;
|
||||
delete params.dir;
|
||||
return 'dir=' + OC.encodePath(dir) + '&' + OC.buildQueryString(params);
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the URL to point to the given dir and view
|
||||
*/
|
||||
_changeUrl: function(view, dir, fileId) {
|
||||
var params = { dir: dir };
|
||||
if (view !== 'files') {
|
||||
params.view = view;
|
||||
} else if (fileId) {
|
||||
params.fileid = fileId;
|
||||
}
|
||||
var currentParams = OC.Util.History.parseUrlQuery();
|
||||
if (currentParams.dir === params.dir && currentParams.view === params.view) {
|
||||
if (currentParams.fileid !== params.fileid) {
|
||||
// if only fileid changed or was added, replace instead of push
|
||||
OC.Util.History.replaceState(this._makeUrlParams(params));
|
||||
return
|
||||
}
|
||||
} else {
|
||||
OC.Util.History.pushState(this._makeUrlParams(params));
|
||||
return
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle showing gridview by default or not
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
_onGridviewChange: function() {
|
||||
const isGridView = this.$showGridView.is(':checked');
|
||||
// only save state if user is logged in
|
||||
if (OC.currentUser) {
|
||||
$.post(OC.generateUrl('/apps/files/api/v1/showgridview'), {
|
||||
show: isGridView,
|
||||
});
|
||||
}
|
||||
this.$showGridView.next('#view-toggle')
|
||||
.removeClass('icon-toggle-filelist icon-toggle-pictures')
|
||||
.addClass(isGridView ? 'icon-toggle-filelist' : 'icon-toggle-pictures')
|
||||
this.$showGridView.next('#view-toggle')
|
||||
.attr('title', isGridView ? t('files', 'Show list view') : t('files', 'Show grid view'))
|
||||
this.$showGridView.attr('aria-label', isGridView ? t('files', 'Show list view') : t('files', 'Show grid view'))
|
||||
|
||||
if (this.currentFileList) {
|
||||
this.currentFileList.setGridView(isGridView);
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
})();
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
// wait for other apps/extensions to register their event handlers and file actions
|
||||
// in the "ready" clause
|
||||
_.defer(function() {
|
||||
OCA.Files.App.initialize();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,352 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2013-2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function() {
|
||||
/**
|
||||
* @class BreadCrumb
|
||||
* @memberof OCA.Files
|
||||
* @classdesc Breadcrumbs that represent the current path.
|
||||
*
|
||||
* @param {Object} [options] options
|
||||
* @param {Function} [options.onClick] click event handler
|
||||
* @param {Function} [options.onDrop] drop event handler
|
||||
* @param {Function} [options.getCrumbUrl] callback that returns
|
||||
* the URL of a given breadcrumb
|
||||
*/
|
||||
var BreadCrumb = function(options){
|
||||
this.$el = $('<nav></nav>');
|
||||
this.$menu = $('<div class="popovermenu menu-center"><ul></ul></div>');
|
||||
|
||||
this.crumbSelector = '.crumb:not(.hidden):not(.crumbhome):not(.crumbmenu)';
|
||||
this.hiddenCrumbSelector = '.crumb.hidden:not(.crumbhome):not(.crumbmenu)';
|
||||
options = options || {};
|
||||
if (options.onClick) {
|
||||
this.onClick = options.onClick;
|
||||
}
|
||||
if (options.onDrop) {
|
||||
this.onDrop = options.onDrop;
|
||||
this.onOver = options.onOver;
|
||||
this.onOut = options.onOut;
|
||||
}
|
||||
if (options.getCrumbUrl) {
|
||||
this.getCrumbUrl = options.getCrumbUrl;
|
||||
}
|
||||
this._detailViews = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* @memberof OCA.Files
|
||||
*/
|
||||
BreadCrumb.prototype = {
|
||||
$el: null,
|
||||
dir: null,
|
||||
dirInfo: null,
|
||||
|
||||
/**
|
||||
* Total width of all breadcrumbs
|
||||
* @type int
|
||||
* @private
|
||||
*/
|
||||
totalWidth: 0,
|
||||
breadcrumbs: [],
|
||||
onClick: null,
|
||||
onDrop: null,
|
||||
onOver: null,
|
||||
onOut: null,
|
||||
|
||||
/**
|
||||
* Sets the directory to be displayed as breadcrumb.
|
||||
* This will re-render the breadcrumb.
|
||||
* @param dir path to be displayed as breadcrumb
|
||||
*/
|
||||
setDirectory: function(dir) {
|
||||
dir = dir.replace(/\\/g, '/');
|
||||
dir = dir || '/';
|
||||
if (dir !== this.dir) {
|
||||
this.dir = dir;
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
setDirectoryInfo: function(dirInfo) {
|
||||
if (dirInfo !== this.dirInfo) {
|
||||
this.dirInfo = dirInfo;
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Backbone.View} detailView
|
||||
*/
|
||||
addDetailView: function(detailView) {
|
||||
this._detailViews.push(detailView);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the full URL to the given directory
|
||||
*
|
||||
* @param {Object.<String, String>} part crumb data as map
|
||||
* @param {number} index crumb index
|
||||
* @return full URL
|
||||
*/
|
||||
getCrumbUrl: function(part, index) {
|
||||
return '#';
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the breadcrumb elements
|
||||
*/
|
||||
render: function() {
|
||||
// Menu is destroyed on every change, we need to init it
|
||||
OC.unregisterMenu($('.crumbmenu > .icon-more'), $('.crumbmenu > .popovermenu'));
|
||||
|
||||
var parts = this._makeCrumbs(this.dir || '/');
|
||||
var $crumb;
|
||||
var $menuItem;
|
||||
this.$el.empty();
|
||||
this.breadcrumbs = [];
|
||||
var $crumbList = $('<ul class="breadcrumb"></ul>');
|
||||
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var part = parts[i];
|
||||
var $image;
|
||||
var $link = $('<a></a>');
|
||||
$crumb = $('<li class="crumb svg"></li>');
|
||||
if(part.dir) {
|
||||
$link.attr('href', this.getCrumbUrl(part, i));
|
||||
}
|
||||
if(part.name) {
|
||||
$link.text(part.name);
|
||||
}
|
||||
$link.addClass(part.linkclass);
|
||||
$crumb.append($link);
|
||||
$crumb.data('dir', part.dir);
|
||||
// Ignore menu button
|
||||
$crumb.data('crumb-id', i - 1);
|
||||
$crumb.addClass(part.class);
|
||||
|
||||
if (part.img) {
|
||||
$image = $('<img class="svg"></img>');
|
||||
$image.attr('src', part.img);
|
||||
$image.attr('alt', part.alt);
|
||||
$link.append($image);
|
||||
}
|
||||
this.breadcrumbs.push($crumb);
|
||||
$crumbList.append($crumb);
|
||||
// Only add feedback if not menu
|
||||
if (this.onClick && i !== 0) {
|
||||
$link.on('click', this.onClick);
|
||||
}
|
||||
}
|
||||
this.$el.append($crumbList);
|
||||
|
||||
// Menu creation
|
||||
this._createMenu();
|
||||
for (var j = 0; j < parts.length; j++) {
|
||||
var menuPart = parts[j];
|
||||
if(menuPart.dir) {
|
||||
$menuItem = $('<li class="crumblist"><a><span class="icon-folder"></span><span></span></a></li>');
|
||||
$menuItem.data('dir', menuPart.dir);
|
||||
$menuItem.find('a').attr('href', this.getCrumbUrl(part, j));
|
||||
$menuItem.find('span:eq(1)').text(menuPart.name);
|
||||
this.$menu.children('ul').append($menuItem);
|
||||
if (this.onClick) {
|
||||
$menuItem.on('click', this.onClick);
|
||||
}
|
||||
}
|
||||
}
|
||||
_.each(this._detailViews, function(view) {
|
||||
view.render({
|
||||
dirInfo: this.dirInfo
|
||||
});
|
||||
$crumb.append(view.$el);
|
||||
$menuItem.append(view.$el.clone(true));
|
||||
}, this);
|
||||
|
||||
// setup drag and drop
|
||||
if (this.onDrop) {
|
||||
this.$el.find('.crumb:not(:last-child):not(.crumbmenu), .crumblist:not(:last-child)').droppable({
|
||||
drop: this.onDrop,
|
||||
over: this.onOver,
|
||||
out: this.onOut,
|
||||
tolerance: 'pointer',
|
||||
hoverClass: 'canDrop',
|
||||
greedy: true
|
||||
});
|
||||
}
|
||||
|
||||
// Menu is destroyed on every change, we need to init it
|
||||
OC.registerMenu($('.crumbmenu > .icon-more'), $('.crumbmenu > .popovermenu'));
|
||||
|
||||
this._resize();
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes a breadcrumb structure based on the given path
|
||||
*
|
||||
* @param {String} dir path to split into a breadcrumb structure
|
||||
* @param {String} [rootIcon=icon-home] icon to use for root
|
||||
* @return {Object.<String, String>} map of {dir: path, name: displayName}
|
||||
*/
|
||||
_makeCrumbs: function(dir, rootIcon) {
|
||||
var crumbs = [];
|
||||
var pathToHere = '';
|
||||
// trim leading and trailing slashes
|
||||
dir = dir.replace(/^\/+|\/+$/g, '');
|
||||
var parts = dir.split('/');
|
||||
if (dir === '') {
|
||||
parts = [];
|
||||
}
|
||||
// menu part
|
||||
crumbs.push({
|
||||
class: 'crumbmenu hidden',
|
||||
linkclass: 'icon-more menutoggle'
|
||||
});
|
||||
// root part
|
||||
crumbs.push({
|
||||
name: t('files', 'Home'),
|
||||
dir: '/',
|
||||
class: 'crumbhome',
|
||||
linkclass: rootIcon || 'icon-home'
|
||||
});
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var part = parts[i];
|
||||
pathToHere = pathToHere + '/' + part;
|
||||
crumbs.push({
|
||||
dir: pathToHere,
|
||||
name: part
|
||||
});
|
||||
}
|
||||
return crumbs;
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate real width based on individual crumbs
|
||||
*
|
||||
* @param {boolean} ignoreHidden ignore hidden crumbs
|
||||
*/
|
||||
getTotalWidth: function(ignoreHidden) {
|
||||
// The width has to be calculated by adding up the width of all the
|
||||
// crumbs; getting the width of the breadcrumb element is not a
|
||||
// valid approach, as the returned value could be clamped to its
|
||||
// parent width.
|
||||
var totalWidth = 0;
|
||||
for (var i = 0; i < this.breadcrumbs.length; i++ ) {
|
||||
var $crumb = $(this.breadcrumbs[i]);
|
||||
if(!$crumb.hasClass('hidden') || ignoreHidden === true) {
|
||||
totalWidth += $crumb.outerWidth(true);
|
||||
}
|
||||
}
|
||||
return totalWidth;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the middle crumb
|
||||
*/
|
||||
_hideCrumb: function() {
|
||||
var length = this.$el.find(this.crumbSelector).length;
|
||||
// Get the middle one floored down
|
||||
var elmt = Math.floor(length / 2 - 0.5);
|
||||
this.$el.find(this.crumbSelector+':eq('+elmt+')').addClass('hidden');
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the crumb to show
|
||||
*/
|
||||
_getCrumbElement: function() {
|
||||
var hidden = this.$el.find(this.hiddenCrumbSelector).length;
|
||||
var shown = this.$el.find(this.crumbSelector).length;
|
||||
// Get the outer one with priority to the highest
|
||||
var elmt = (1 - shown % 2) * (hidden - 1);
|
||||
return this.$el.find(this.hiddenCrumbSelector + ':eq('+elmt+')');
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the middle crumb
|
||||
*/
|
||||
_showCrumb: function() {
|
||||
if(this.$el.find(this.hiddenCrumbSelector).length === 1) {
|
||||
this.$el.find(this.hiddenCrumbSelector).removeClass('hidden');
|
||||
}
|
||||
this._getCrumbElement().removeClass('hidden');
|
||||
},
|
||||
|
||||
/**
|
||||
* Create and append the popovermenu
|
||||
*/
|
||||
_createMenu: function() {
|
||||
this.$el.find('.crumbmenu').append(this.$menu);
|
||||
this.$menu.children('ul').empty();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the popovermenu
|
||||
*/
|
||||
_updateMenu: function() {
|
||||
var menuItems = this.$el.find(this.hiddenCrumbSelector);
|
||||
|
||||
this.$menu.find('li').addClass('in-breadcrumb');
|
||||
for (var i = 0; i < menuItems.length; i++) {
|
||||
var crumbId = $(menuItems[i]).data('crumb-id');
|
||||
this.$menu.find('li:eq('+crumbId+')').removeClass('in-breadcrumb');
|
||||
}
|
||||
},
|
||||
|
||||
_resize: function() {
|
||||
|
||||
if (this.breadcrumbs.length <= 2) {
|
||||
// home & menu
|
||||
return;
|
||||
}
|
||||
|
||||
// Always hide the menu to ensure that it does not interfere with
|
||||
// the width calculations; otherwise, the result could be different
|
||||
// depending on whether the menu was previously being shown or not.
|
||||
this.$el.find('.crumbmenu').addClass('hidden');
|
||||
|
||||
// Show the crumbs to compress the siblings before hiding again the
|
||||
// crumbs. This is needed when the siblings expand to fill all the
|
||||
// available width, as in that case their old width would limit the
|
||||
// available width for the crumbs.
|
||||
// Note that the crumbs shown always overflow the parent width
|
||||
// (except, of course, when they all fit in).
|
||||
while (this.$el.find(this.hiddenCrumbSelector).length > 0
|
||||
&& Math.round(this.getTotalWidth()) <= Math.round(this.$el.parent().width())) {
|
||||
this._showCrumb();
|
||||
}
|
||||
|
||||
var siblingsWidth = 0;
|
||||
this.$el.prevAll(':visible').each(function () {
|
||||
siblingsWidth += $(this).outerWidth(true);
|
||||
});
|
||||
this.$el.nextAll(':visible').each(function () {
|
||||
siblingsWidth += $(this).outerWidth(true);
|
||||
});
|
||||
|
||||
var availableWidth = this.$el.parent().width() - siblingsWidth;
|
||||
|
||||
// If container is smaller than content
|
||||
// AND if there are crumbs left to hide
|
||||
while (Math.round(this.getTotalWidth()) > Math.round(availableWidth)
|
||||
&& this.$el.find(this.crumbSelector).length > 0) {
|
||||
// As soon as one of the crumbs is hidden the menu will be
|
||||
// shown. This is needed for proper results in further width
|
||||
// checks.
|
||||
// Note that the menu is not shown only when all the crumbs were
|
||||
// being shown and they all fit the available space; if any of
|
||||
// the crumbs was not being shown then those shown would
|
||||
// overflow the available width, so at least one will be hidden
|
||||
// and thus the menu will be shown.
|
||||
this.$el.find('.crumbmenu').removeClass('hidden');
|
||||
this._hideCrumb();
|
||||
}
|
||||
|
||||
this._updateMenu();
|
||||
}
|
||||
};
|
||||
|
||||
OCA.Files.BreadCrumb = BreadCrumb;
|
||||
})();
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function() {
|
||||
/**
|
||||
* @class OCA.Files.DetailFileInfoView
|
||||
* @classdesc
|
||||
*
|
||||
* Displays a block of details about the file info.
|
||||
*
|
||||
*/
|
||||
var DetailFileInfoView = OC.Backbone.View.extend({
|
||||
tagName: 'div',
|
||||
className: 'detailFileInfoView',
|
||||
|
||||
_template: null,
|
||||
|
||||
/**
|
||||
* returns the jQuery object for HTML output
|
||||
*
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
get$: function() {
|
||||
return this.$el;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the file info to be displayed in the view
|
||||
*
|
||||
* @param {OCA.Files.FileInfo} fileInfo file info to set
|
||||
*/
|
||||
setFileInfo: function(fileInfo) {
|
||||
this.model = fileInfo;
|
||||
this.render();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the file info.
|
||||
*
|
||||
* @return {OCA.Files.FileInfo} file info
|
||||
*/
|
||||
getFileInfo: function() {
|
||||
return this.model;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.Files.DetailFileInfoView = DetailFileInfoView;
|
||||
})();
|
||||
|
||||
|
|
@ -1,284 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function() {
|
||||
/**
|
||||
* @class OCA.Files.DetailsView
|
||||
* @classdesc
|
||||
*
|
||||
* The details view show details about a selected file.
|
||||
*
|
||||
*/
|
||||
var DetailsView = OC.Backbone.View.extend({
|
||||
id: 'app-sidebar',
|
||||
tabName: 'div',
|
||||
className: 'detailsView scroll-container',
|
||||
|
||||
/**
|
||||
* List of detail tab views
|
||||
*
|
||||
* @type Array<OCA.Files.DetailTabView>
|
||||
*/
|
||||
_tabViews: [],
|
||||
|
||||
/**
|
||||
* List of detail file info views
|
||||
*
|
||||
* @type Array<OCA.Files.DetailFileInfoView>
|
||||
*/
|
||||
_detailFileInfoViews: [],
|
||||
|
||||
/**
|
||||
* Id of the currently selected tab
|
||||
*
|
||||
* @type string
|
||||
*/
|
||||
_currentTabId: null,
|
||||
|
||||
/**
|
||||
* Dirty flag, whether the view needs to be rerendered
|
||||
*/
|
||||
_dirty: false,
|
||||
|
||||
events: {
|
||||
'click a.close': '_onClose',
|
||||
'click .tabHeaders .tabHeader': '_onClickTab',
|
||||
'keyup .tabHeaders .tabHeader': '_onKeyboardActivateTab'
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the details view
|
||||
*/
|
||||
initialize: function() {
|
||||
this._tabViews = [];
|
||||
this._detailFileInfoViews = [];
|
||||
|
||||
this._dirty = true;
|
||||
},
|
||||
|
||||
_onClose: function(event) {
|
||||
OC.Apps.hideAppSidebar(this.$el);
|
||||
event.preventDefault();
|
||||
},
|
||||
|
||||
_onClickTab: function(e) {
|
||||
var $target = $(e.target);
|
||||
e.preventDefault();
|
||||
if (!$target.hasClass('tabHeader')) {
|
||||
$target = $target.closest('.tabHeader');
|
||||
}
|
||||
var tabId = $target.attr('data-tabid');
|
||||
if (_.isUndefined(tabId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectTab(tabId);
|
||||
},
|
||||
|
||||
_onKeyboardActivateTab: function (event) {
|
||||
if (event.key === " " || event.key === "Enter") {
|
||||
this._onClickTab(event);
|
||||
}
|
||||
},
|
||||
|
||||
template: function(vars) {
|
||||
return OCA.Files.Templates['detailsview'](vars);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders this details view
|
||||
*/
|
||||
render: function() {
|
||||
var templateVars = {
|
||||
closeLabel: t('files', 'Close')
|
||||
};
|
||||
|
||||
this._tabViews = this._tabViews.sort(function(tabA, tabB) {
|
||||
var orderA = tabA.order || 0;
|
||||
var orderB = tabB.order || 0;
|
||||
if (orderA === orderB) {
|
||||
return OC.Util.naturalSortCompare(tabA.getLabel(), tabB.getLabel());
|
||||
}
|
||||
return orderA - orderB;
|
||||
});
|
||||
|
||||
templateVars.tabHeaders = _.map(this._tabViews, function(tabView, i) {
|
||||
return {
|
||||
tabId: tabView.id,
|
||||
label: tabView.getLabel(),
|
||||
tabIcon: tabView.getIcon()
|
||||
};
|
||||
});
|
||||
|
||||
this.$el.html(this.template(templateVars));
|
||||
|
||||
var $detailsContainer = this.$el.find('.detailFileInfoContainer');
|
||||
|
||||
// render details
|
||||
_.each(this._detailFileInfoViews, function(detailView) {
|
||||
$detailsContainer.append(detailView.get$());
|
||||
});
|
||||
|
||||
if (!this._currentTabId && this._tabViews.length > 0) {
|
||||
this._currentTabId = this._tabViews[0].id;
|
||||
}
|
||||
|
||||
this.selectTab(this._currentTabId);
|
||||
|
||||
this._updateTabVisibilities();
|
||||
|
||||
this._dirty = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Selects the given tab by id
|
||||
*
|
||||
* @param {string} tabId tab id
|
||||
*/
|
||||
selectTab: function(tabId) {
|
||||
if (!tabId) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tabView = _.find(this._tabViews, function(tab) {
|
||||
return tab.id === tabId;
|
||||
});
|
||||
|
||||
if (!tabView) {
|
||||
console.warn('Details view tab with id "' + tabId + '" not found');
|
||||
return;
|
||||
}
|
||||
|
||||
this._currentTabId = tabId;
|
||||
|
||||
var $tabsContainer = this.$el.find('.tabsContainer');
|
||||
var $tabEl = $tabsContainer.find('#' + tabId);
|
||||
|
||||
// hide other tabs
|
||||
$tabsContainer.find('.tab').addClass('hidden');
|
||||
|
||||
$tabsContainer.attr('class', 'tabsContainer');
|
||||
$tabsContainer.addClass(tabView.getTabsContainerExtraClasses());
|
||||
|
||||
// tab already rendered ?
|
||||
if (!$tabEl.length) {
|
||||
// render tab
|
||||
$tabsContainer.append(tabView.$el);
|
||||
$tabEl = tabView.$el;
|
||||
}
|
||||
|
||||
// this should trigger tab rendering
|
||||
tabView.setFileInfo(this.model);
|
||||
|
||||
$tabEl.removeClass('hidden');
|
||||
|
||||
// update tab headers
|
||||
var $tabHeaders = this.$el.find('.tabHeaders li');
|
||||
$tabHeaders.removeClass('selected');
|
||||
$tabHeaders.filterAttr('data-tabid', tabView.id).addClass('selected');
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the file info to be displayed in the view
|
||||
*
|
||||
* @param {OCA.Files.FileInfoModel} fileInfo file info to set
|
||||
*/
|
||||
setFileInfo: function(fileInfo) {
|
||||
this.model = fileInfo;
|
||||
|
||||
if (this._dirty) {
|
||||
this.render();
|
||||
} else {
|
||||
this._updateTabVisibilities();
|
||||
}
|
||||
|
||||
if (this._currentTabId) {
|
||||
// only update current tab, others will be updated on-demand
|
||||
var tabId = this._currentTabId;
|
||||
var tabView = _.find(this._tabViews, function(tab) {
|
||||
return tab.id === tabId;
|
||||
});
|
||||
tabView.setFileInfo(fileInfo);
|
||||
}
|
||||
|
||||
_.each(this._detailFileInfoViews, function(detailView) {
|
||||
detailView.setFileInfo(fileInfo);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Update tab headers based on the current model
|
||||
*/
|
||||
_updateTabVisibilities: function() {
|
||||
// update tab header visibilities
|
||||
var self = this;
|
||||
var deselect = false;
|
||||
var countVisible = 0;
|
||||
var $tabHeaders = this.$el.find('.tabHeaders li');
|
||||
_.each(this._tabViews, function(tabView) {
|
||||
var isVisible = tabView.canDisplay(self.model);
|
||||
if (isVisible) {
|
||||
countVisible += 1;
|
||||
}
|
||||
if (!isVisible && self._currentTabId === tabView.id) {
|
||||
deselect = true;
|
||||
}
|
||||
$tabHeaders.filterAttr('data-tabid', tabView.id).toggleClass('hidden', !isVisible);
|
||||
});
|
||||
|
||||
// hide the whole container if there is only one tab
|
||||
this.$el.find('.tabHeaders').toggleClass('hidden', countVisible <= 1);
|
||||
|
||||
if (deselect) {
|
||||
// select the first visible tab instead
|
||||
var visibleTabId = this.$el.find('.tabHeader:not(.hidden):first').attr('data-tabid');
|
||||
this.selectTab(visibleTabId);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the file info.
|
||||
*
|
||||
* @return {OCA.Files.FileInfoModel} file info
|
||||
*/
|
||||
getFileInfo: function() {
|
||||
return this.model;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a tab in the tab view
|
||||
*
|
||||
* @param {OCA.Files.DetailTabView} tab view
|
||||
*/
|
||||
addTabView: function(tabView) {
|
||||
this._tabViews.push(tabView);
|
||||
this._dirty = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a detail view for file info.
|
||||
*
|
||||
* @param {OCA.Files.DetailFileInfoView} detail view
|
||||
*/
|
||||
addDetailView: function(detailView) {
|
||||
this._detailFileInfoViews.push(detailView);
|
||||
this._dirty = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array with the added DetailFileInfoViews.
|
||||
*
|
||||
* @return Array<OCA.Files.DetailFileInfoView> an array with the added
|
||||
* DetailFileInfoViews.
|
||||
*/
|
||||
getDetailViews: function() {
|
||||
return [].concat(this._detailFileInfoViews);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.Files.DetailsView = DetailsView;
|
||||
})();
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @class OCA.Files.DetailTabView
|
||||
* @classdesc
|
||||
*
|
||||
* Base class for tab views to display file information.
|
||||
*
|
||||
*/
|
||||
var DetailTabView = OC.Backbone.View.extend({
|
||||
tag: 'div',
|
||||
|
||||
className: 'tab',
|
||||
|
||||
/**
|
||||
* Tab label
|
||||
*/
|
||||
_label: null,
|
||||
|
||||
_template: null,
|
||||
|
||||
initialize: function(options) {
|
||||
options = options || {};
|
||||
if (!this.id) {
|
||||
this.id = 'detailTabView' + DetailTabView._TAB_COUNT;
|
||||
DetailTabView._TAB_COUNT++;
|
||||
}
|
||||
if (options.order) {
|
||||
this.order = options.order || 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the extra CSS classes used by the tabs container when this
|
||||
* tab is the selected one.
|
||||
*
|
||||
* In general you should not extend this method, as tabs should not
|
||||
* modify the classes of its container; this is reserved as a last
|
||||
* resort for very specific cases in which there is no other way to get
|
||||
* the proper style or behaviour.
|
||||
*
|
||||
* @return {String} space-separated CSS classes
|
||||
*/
|
||||
getTabsContainerExtraClasses: function() {
|
||||
return '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the tab label
|
||||
*
|
||||
* @return {String} label
|
||||
*/
|
||||
getLabel: function() {
|
||||
return 'Tab ' + this.id;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the tab label
|
||||
*
|
||||
* @return {String}|{null} icon class
|
||||
*/
|
||||
getIcon: function() {
|
||||
return null
|
||||
},
|
||||
|
||||
/**
|
||||
* returns the jQuery object for HTML output
|
||||
*
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
get$: function() {
|
||||
return this.$el;
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders this details view
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
render: function() {
|
||||
// to be implemented in subclass
|
||||
// FIXME: code is only for testing
|
||||
this.$el.html('<div>Hello ' + this.id + '</div>');
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the file info to be displayed in the view
|
||||
*
|
||||
* @param {OCA.Files.FileInfoModel} fileInfo file info to set
|
||||
*/
|
||||
setFileInfo: function(fileInfo) {
|
||||
if (this.model !== fileInfo) {
|
||||
this.model = fileInfo;
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the file info.
|
||||
*
|
||||
* @return {OCA.Files.FileInfoModel} file info
|
||||
*/
|
||||
getFileInfo: function() {
|
||||
return this.model;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the next page of results
|
||||
*/
|
||||
nextPage: function() {
|
||||
// load the next page, if applicable
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether the current tab is able to display
|
||||
* the given file info, for example based on mime type.
|
||||
*
|
||||
* @param {OCA.Files.FileInfoModel} fileInfo file info model
|
||||
* @return {boolean} whether to display this tab
|
||||
*/
|
||||
canDisplay: function(fileInfo) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
DetailTabView._TAB_COUNT = 0;
|
||||
|
||||
OCA.Files = OCA.Files || {};
|
||||
|
||||
OCA.Files.DetailTabView = DetailTabView;
|
||||
})();
|
||||
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,915 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2012-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* Construct a new FileActions instance
|
||||
* @constructs FileActions
|
||||
* @memberof OCA.Files
|
||||
*/
|
||||
var FileActions = function() {
|
||||
this.initialize();
|
||||
};
|
||||
FileActions.TYPE_DROPDOWN = 0;
|
||||
FileActions.TYPE_INLINE = 1;
|
||||
FileActions.prototype = {
|
||||
/** @lends FileActions.prototype */
|
||||
actions: {},
|
||||
defaults: {},
|
||||
icons: {},
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
currentFile: null,
|
||||
|
||||
/**
|
||||
* Dummy jquery element, for events
|
||||
*/
|
||||
$el: null,
|
||||
|
||||
_fileActionTriggerTemplate: null,
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
initialize: function() {
|
||||
this.clear();
|
||||
// abusing jquery for events until we get a real event lib
|
||||
this.$el = $('<div class="dummy-fileactions hidden"></div>');
|
||||
$('body').append(this.$el);
|
||||
|
||||
this._showMenuClosure = _.bind(this._showMenu, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an event handler
|
||||
*
|
||||
* @param {String} eventName event name
|
||||
* @param {Function} callback
|
||||
*/
|
||||
on: function(eventName, callback) {
|
||||
this.$el.on(eventName, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes an event handler
|
||||
*
|
||||
* @param {String} eventName event name
|
||||
* @param {Function} callback
|
||||
*/
|
||||
off: function(eventName, callback) {
|
||||
this.$el.off(eventName, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifies the event handlers
|
||||
*
|
||||
* @param {String} eventName event name
|
||||
* @param {Object} data data
|
||||
*/
|
||||
_notifyUpdateListeners: function(eventName, data) {
|
||||
this.$el.trigger(new $.Event(eventName, data));
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges the actions from the given fileActions into
|
||||
* this instance.
|
||||
*
|
||||
* @param {OCA.Files.FileActions} fileActions instance of OCA.Files.FileActions
|
||||
*/
|
||||
merge: function(fileActions) {
|
||||
var self = this;
|
||||
// merge first level to avoid unintended overwriting
|
||||
_.each(fileActions.actions, function(sourceMimeData, mime) {
|
||||
var targetMimeData = self.actions[mime];
|
||||
if (!targetMimeData) {
|
||||
targetMimeData = {};
|
||||
}
|
||||
self.actions[mime] = _.extend(targetMimeData, sourceMimeData);
|
||||
});
|
||||
|
||||
this.defaults = _.extend(this.defaults, fileActions.defaults);
|
||||
this.icons = _.extend(this.icons, fileActions.icons);
|
||||
},
|
||||
/**
|
||||
* @deprecated use #registerAction() instead
|
||||
*/
|
||||
register: function(mime, name, permissions, icon, action, displayName) {
|
||||
return this.registerAction({
|
||||
name: name,
|
||||
mime: mime,
|
||||
permissions: permissions,
|
||||
icon: icon,
|
||||
actionHandler: action,
|
||||
displayName: displayName || name
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Register action
|
||||
*
|
||||
* @param {OCA.Files.FileAction} action object
|
||||
*/
|
||||
registerAction: function (action) {
|
||||
var mime = action.mime;
|
||||
var name = action.name;
|
||||
var actionSpec = {
|
||||
action: function(fileName, context) {
|
||||
// Actions registered in one FileAction may be executed on a
|
||||
// different one (for example, due to the "merge" function),
|
||||
// so the listeners have to be updated on the FileActions
|
||||
// from the context instead of on the one in which it was
|
||||
// originally registered.
|
||||
if (context && context.fileActions) {
|
||||
context.fileActions._notifyUpdateListeners('beforeTriggerAction', {action: actionSpec, fileName: fileName, context: context});
|
||||
}
|
||||
|
||||
action.actionHandler(fileName, context);
|
||||
|
||||
if (context && context.fileActions) {
|
||||
context.fileActions._notifyUpdateListeners('afterTriggerAction', {action: actionSpec, fileName: fileName, context: context});
|
||||
}
|
||||
},
|
||||
name: name,
|
||||
displayName: action.displayName,
|
||||
mime: mime,
|
||||
filename: action.filename,
|
||||
order: action.order || 0,
|
||||
icon: action.icon,
|
||||
iconClass: action.iconClass,
|
||||
permissions: action.permissions,
|
||||
type: action.type || FileActions.TYPE_DROPDOWN,
|
||||
altText: action.altText || ''
|
||||
};
|
||||
if (_.isUndefined(action.displayName)) {
|
||||
actionSpec.displayName = t('files', name);
|
||||
}
|
||||
if (_.isFunction(action.render)) {
|
||||
actionSpec.render = action.render;
|
||||
}
|
||||
if (_.isFunction(action.shouldRender)) {
|
||||
actionSpec.shouldRender = action.shouldRender;
|
||||
}
|
||||
if (!this.actions[mime]) {
|
||||
this.actions[mime] = {};
|
||||
}
|
||||
this.actions[mime][name] = actionSpec;
|
||||
this.icons[name] = action.icon;
|
||||
this._notifyUpdateListeners('registerAction', {action: action});
|
||||
},
|
||||
/**
|
||||
* Clears all registered file actions.
|
||||
*/
|
||||
clear: function() {
|
||||
this.actions = {};
|
||||
this.defaults = {};
|
||||
this.icons = {};
|
||||
this.currentFile = null;
|
||||
},
|
||||
/**
|
||||
* Sets the default action for a given mime type.
|
||||
*
|
||||
* @param {String} mime mime type
|
||||
* @param {String} name action name
|
||||
*/
|
||||
setDefault: function (mime, name) {
|
||||
this.defaults[mime] = name;
|
||||
this._notifyUpdateListeners('setDefault', {defaultAction: {mime: mime, name: name}});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a map of file actions handlers matching the given conditions
|
||||
*
|
||||
* @param {string} mime mime type
|
||||
* @param {string} type "dir" or "file"
|
||||
* @param {number} permissions permissions
|
||||
* @param {string} filename filename
|
||||
*
|
||||
* @return {Object.<string,OCA.Files.FileActions~actionHandler>} map of action name to action spec
|
||||
*/
|
||||
get: function(mime, type, permissions, filename) {
|
||||
var actions = this.getActions(mime, type, permissions, filename);
|
||||
var filteredActions = {};
|
||||
$.each(actions, function (name, action) {
|
||||
filteredActions[name] = action.action;
|
||||
});
|
||||
return filteredActions;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array of file actions matching the given conditions
|
||||
*
|
||||
* @param {string} mime mime type
|
||||
* @param {string} type "dir" or "file"
|
||||
* @param {number} permissions permissions
|
||||
* @param {string} filename filename
|
||||
*
|
||||
* @return {Array.<OCA.Files.FileAction>} array of action specs
|
||||
*/
|
||||
getActions: function(mime, type, permissions, filename) {
|
||||
var actions = {};
|
||||
if (this.actions.all) {
|
||||
actions = $.extend(actions, this.actions.all);
|
||||
}
|
||||
if (type) {//type is 'dir' or 'file'
|
||||
if (this.actions[type]) {
|
||||
actions = $.extend(actions, this.actions[type]);
|
||||
}
|
||||
}
|
||||
if (mime) {
|
||||
var mimePart = mime.substr(0, mime.indexOf('/'));
|
||||
if (this.actions[mimePart]) {
|
||||
actions = $.extend(actions, this.actions[mimePart]);
|
||||
}
|
||||
if (this.actions[mime]) {
|
||||
actions = $.extend(actions, this.actions[mime]);
|
||||
}
|
||||
}
|
||||
|
||||
var filteredActions = {};
|
||||
var self = this;
|
||||
$.each(actions, function(name, action) {
|
||||
if (self.allowedPermissions(action.permissions, permissions) &&
|
||||
self.allowedFilename(action.filename, filename)) {
|
||||
filteredActions[name] = action;
|
||||
}
|
||||
});
|
||||
|
||||
return filteredActions;
|
||||
},
|
||||
|
||||
|
||||
allowedPermissions: function(actionPermissions, permissions) {
|
||||
return (actionPermissions === OC.PERMISSION_NONE || (actionPermissions & permissions));
|
||||
},
|
||||
|
||||
allowedFilename: function(actionFilename, filename) {
|
||||
return (!filename || filename === '' || !actionFilename
|
||||
|| actionFilename === '' || actionFilename === filename);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the default file action handler for the given conditions
|
||||
*
|
||||
* @param {string} mime mime type
|
||||
* @param {string} type "dir" or "file"
|
||||
* @param {number} permissions permissions
|
||||
*
|
||||
* @return {OCA.Files.FileActions~actionHandler} action handler
|
||||
*
|
||||
* @deprecated use getDefaultFileAction instead
|
||||
*/
|
||||
getDefault: function (mime, type, permissions) {
|
||||
var defaultActionSpec = this.getDefaultFileAction(mime, type, permissions);
|
||||
if (defaultActionSpec) {
|
||||
return defaultActionSpec.action;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the default file action handler for the current file
|
||||
*
|
||||
* @return {OCA.Files.FileActions~actionSpec} action spec
|
||||
* @since 8.2
|
||||
*/
|
||||
getCurrentDefaultFileAction: function() {
|
||||
var mime = this.getCurrentMimeType();
|
||||
var type = this.getCurrentType();
|
||||
var permissions = this.getCurrentPermissions();
|
||||
return this.getDefaultFileAction(mime, type, permissions);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the default file action handler for the given conditions
|
||||
*
|
||||
* @param {string} mime mime type
|
||||
* @param {string} type "dir" or "file"
|
||||
* @param {number} permissions permissions
|
||||
*
|
||||
* @return {OCA.Files.FileActions~actionSpec} action spec
|
||||
* @since 8.2
|
||||
*/
|
||||
getDefaultFileAction: function(mime, type, permissions) {
|
||||
var mimePart;
|
||||
if (mime) {
|
||||
mimePart = mime.substr(0, mime.indexOf('/'));
|
||||
}
|
||||
var name = false;
|
||||
if (mime && this.defaults[mime]) {
|
||||
name = this.defaults[mime];
|
||||
} else if (mime && this.defaults[mimePart]) {
|
||||
name = this.defaults[mimePart];
|
||||
} else if (type && this.defaults[type]) {
|
||||
name = this.defaults[type];
|
||||
} else {
|
||||
name = this.defaults.all;
|
||||
}
|
||||
var actions = this.getActions(mime, type, permissions);
|
||||
return actions[name];
|
||||
},
|
||||
|
||||
/**
|
||||
* Default function to render actions
|
||||
*
|
||||
* @param {OCA.Files.FileAction} actionSpec file action spec
|
||||
* @param {boolean} isDefault true if the action is a default one,
|
||||
* false otherwise
|
||||
* @param {OCA.Files.FileActionContext} context action context
|
||||
*/
|
||||
_defaultRenderAction: function(actionSpec, isDefault, context) {
|
||||
if (!isDefault) {
|
||||
var params = {
|
||||
name: actionSpec.name,
|
||||
nameLowerCase: actionSpec.name.toLowerCase(),
|
||||
displayName: actionSpec.displayName,
|
||||
icon: actionSpec.icon,
|
||||
iconClass: actionSpec.iconClass,
|
||||
altText: actionSpec.altText,
|
||||
hasDisplayName: !!actionSpec.displayName
|
||||
};
|
||||
if (_.isFunction(actionSpec.icon)) {
|
||||
params.icon = actionSpec.icon(context.$file.attr('data-file'), context);
|
||||
}
|
||||
if (_.isFunction(actionSpec.iconClass)) {
|
||||
params.iconClass = actionSpec.iconClass(context.$file.attr('data-file'), context);
|
||||
}
|
||||
|
||||
var $actionLink = this._makeActionLink(params, context);
|
||||
context.$file.find('a.name>span.fileactions').append($actionLink);
|
||||
$actionLink.addClass('permanent');
|
||||
return $actionLink;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the action link element
|
||||
*
|
||||
* @param {Object} params action params
|
||||
*/
|
||||
_makeActionLink: function(params) {
|
||||
return $(OCA.Files.Templates['file_action_trigger'](params));
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays the file actions dropdown menu
|
||||
*
|
||||
* @param {string} fileName file name
|
||||
* @param {OCA.Files.FileActionContext} context rendering context
|
||||
*/
|
||||
_showMenu: function(fileName, context) {
|
||||
var menu;
|
||||
var $trigger = context.$file.closest('tr').find('.fileactions .action-menu');
|
||||
$trigger.addClass('open');
|
||||
$trigger.attr('aria-expanded', 'true');
|
||||
|
||||
menu = new OCA.Files.FileActionsMenu();
|
||||
|
||||
context.$file.find('td.filename').append(menu.$el);
|
||||
|
||||
menu.$el.on('afterHide', function() {
|
||||
context.$file.removeClass('mouseOver');
|
||||
$trigger.removeClass('open');
|
||||
$trigger.attr('aria-expanded', 'false');
|
||||
menu.remove();
|
||||
});
|
||||
|
||||
context.$file.addClass('mouseOver');
|
||||
menu.show(context);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the menu trigger on the given file list row
|
||||
*
|
||||
* @param {Object} $tr file list row element
|
||||
* @param {OCA.Files.FileActionContext} context rendering context
|
||||
*/
|
||||
_renderMenuTrigger: function($tr, context) {
|
||||
// remove previous
|
||||
$tr.find('.action-menu').remove();
|
||||
|
||||
var $el = this._renderInlineAction({
|
||||
name: 'menu',
|
||||
displayName: '',
|
||||
iconClass: 'icon-more',
|
||||
altText: t('files', 'Actions'),
|
||||
action: this._showMenuClosure
|
||||
}, false, context);
|
||||
|
||||
$el.addClass('permanent');
|
||||
$el.attr('aria-expanded', 'false');
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the action element by calling actionSpec.render() and
|
||||
* registers the click event to process the action.
|
||||
*
|
||||
* @param {OCA.Files.FileAction} actionSpec file action to render
|
||||
* @param {boolean} isDefault true if the action is a default action,
|
||||
* false otherwise
|
||||
* @param {OCA.Files.FileActionContext} context rendering context
|
||||
*/
|
||||
_renderInlineAction: function(actionSpec, isDefault, context) {
|
||||
if (actionSpec.shouldRender) {
|
||||
if (!actionSpec.shouldRender(context)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
var renderFunc = actionSpec.render || _.bind(this._defaultRenderAction, this);
|
||||
var $actionEl = renderFunc(actionSpec, isDefault, context);
|
||||
if (!$actionEl || !$actionEl.length) {
|
||||
return;
|
||||
}
|
||||
$actionEl.on(
|
||||
'click', {
|
||||
a: null
|
||||
},
|
||||
function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if ($actionEl.hasClass('open')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $file = $(event.target).closest('tr');
|
||||
if ($file.hasClass('busy')) {
|
||||
return;
|
||||
}
|
||||
var currentFile = $file.find('td.filename');
|
||||
var fileName = $file.attr('data-file');
|
||||
|
||||
context.fileActions.currentFile = currentFile;
|
||||
|
||||
var callContext = _.extend({}, context);
|
||||
|
||||
if (!context.dir && context.fileList) {
|
||||
callContext.dir = $file.attr('data-path') || context.fileList.getCurrentDirectory();
|
||||
}
|
||||
|
||||
if (!context.fileInfoModel && context.fileList) {
|
||||
callContext.fileInfoModel = context.fileList.getModelForFile(fileName);
|
||||
if (!callContext.fileInfoModel) {
|
||||
console.warn('No file info model found for file "' + fileName + '"');
|
||||
}
|
||||
}
|
||||
|
||||
actionSpec.action(
|
||||
fileName,
|
||||
callContext
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return $actionEl;
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger the given action on the given file.
|
||||
*
|
||||
* @param {string} actionName action name
|
||||
* @param {OCA.Files.FileInfoModel} fileInfoModel file info model
|
||||
* @param {OCA.Files.FileList} [fileList] file list, for compatibility with older action handlers [DEPRECATED]
|
||||
*
|
||||
* @return {boolean} true if the action handler was called, false otherwise
|
||||
*
|
||||
* @since 8.2
|
||||
*/
|
||||
triggerAction: function(actionName, fileInfoModel, fileList) {
|
||||
var actionFunc;
|
||||
var actions = this.get(
|
||||
fileInfoModel.get('mimetype'),
|
||||
fileInfoModel.isDirectory() ? 'dir' : 'file',
|
||||
fileInfoModel.get('permissions'),
|
||||
fileInfoModel.get('name')
|
||||
);
|
||||
|
||||
if (actionName) {
|
||||
actionFunc = actions[actionName];
|
||||
} else {
|
||||
actionFunc = this.getDefault(
|
||||
fileInfoModel.get('mimetype'),
|
||||
fileInfoModel.isDirectory() ? 'dir' : 'file',
|
||||
fileInfoModel.get('permissions')
|
||||
);
|
||||
}
|
||||
|
||||
if (!actionFunc) {
|
||||
actionFunc = actions['Download'];
|
||||
}
|
||||
|
||||
if (!actionFunc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var context = {
|
||||
fileActions: this,
|
||||
fileInfoModel: fileInfoModel,
|
||||
dir: fileInfoModel.get('path')
|
||||
};
|
||||
|
||||
var fileName = fileInfoModel.get('name');
|
||||
this.currentFile = fileName;
|
||||
|
||||
if (fileList) {
|
||||
// compatibility with action handlers that expect these
|
||||
context.fileList = fileList;
|
||||
context.$file = fileList.findFileEl(fileName);
|
||||
}
|
||||
|
||||
actionFunc(fileName, context);
|
||||
},
|
||||
|
||||
/**
|
||||
* Display file actions for the given element
|
||||
* @param parent "td" element of the file for which to display actions
|
||||
* @param triggerEvent if true, triggers the fileActionsReady on the file
|
||||
* list afterwards (false by default)
|
||||
* @param fileList OCA.Files.FileList instance on which the action is
|
||||
* done, defaults to OCA.Files.App.fileList
|
||||
*/
|
||||
display: function (parent, triggerEvent, fileList) {
|
||||
if (!fileList) {
|
||||
console.warn('FileActions.display() MUST be called with a OCA.Files.FileList instance');
|
||||
return;
|
||||
}
|
||||
this.currentFile = parent;
|
||||
var self = this;
|
||||
var $tr = parent.closest('tr');
|
||||
var actions = this.getActions(
|
||||
this.getCurrentMimeType(),
|
||||
this.getCurrentType(),
|
||||
this.getCurrentPermissions(),
|
||||
this.getCurrentFile()
|
||||
);
|
||||
var nameLinks;
|
||||
if ($tr.data('renaming')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// recreate fileactions container
|
||||
nameLinks = parent.children('a.name');
|
||||
nameLinks.find('.fileactions, .nametext .action').remove();
|
||||
nameLinks.append('<span class="fileactions"></span>');
|
||||
var defaultAction = this.getDefaultFileAction(
|
||||
this.getCurrentMimeType(),
|
||||
this.getCurrentType(),
|
||||
this.getCurrentPermissions()
|
||||
);
|
||||
|
||||
var context = {
|
||||
$file: $tr,
|
||||
fileActions: this,
|
||||
fileList: fileList
|
||||
};
|
||||
|
||||
$.each(actions, function (name, actionSpec) {
|
||||
if (actionSpec.type === FileActions.TYPE_INLINE) {
|
||||
self._renderInlineAction(
|
||||
actionSpec,
|
||||
defaultAction && actionSpec.name === defaultAction.name,
|
||||
context
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function objectValues(obj) {
|
||||
var res = [];
|
||||
for (var i in obj) {
|
||||
if (obj.hasOwnProperty(i)) {
|
||||
res.push(obj[i]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
// polyfill
|
||||
if (!Object.values) {
|
||||
Object.values = objectValues;
|
||||
}
|
||||
|
||||
var menuActions = Object.values(actions).filter(function (action) {
|
||||
return action.type !== OCA.Files.FileActions.TYPE_INLINE && (!defaultAction || action.name !== defaultAction.name)
|
||||
});
|
||||
// do not render the menu if nothing is in it
|
||||
if (menuActions.length > 0) {
|
||||
this._renderMenuTrigger($tr, context);
|
||||
}
|
||||
|
||||
if (triggerEvent){
|
||||
fileList.$fileList.trigger(jQuery.Event("fileActionsReady", {fileList: fileList, $files: $tr}));
|
||||
}
|
||||
},
|
||||
getCurrentFile: function () {
|
||||
return this.currentFile.parent().attr('data-file');
|
||||
},
|
||||
getCurrentMimeType: function () {
|
||||
return this.currentFile.parent().attr('data-mime');
|
||||
},
|
||||
getCurrentType: function () {
|
||||
return this.currentFile.parent().attr('data-type');
|
||||
},
|
||||
getCurrentPermissions: function () {
|
||||
return this.currentFile.parent().data('permissions');
|
||||
},
|
||||
|
||||
/**
|
||||
* Register the actions that are used by default for the files app.
|
||||
*/
|
||||
registerDefaultActions: function() {
|
||||
this.registerAction({
|
||||
name: 'Download',
|
||||
displayName: t('files', 'Download'),
|
||||
order: -20,
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
iconClass: 'icon-download',
|
||||
actionHandler: function (filename, context) {
|
||||
var dir = context.dir || context.fileList.getCurrentDirectory();
|
||||
var isDir = context.$file.attr('data-type') === 'dir';
|
||||
var url = context.fileList.getDownloadUrl(filename, dir, isDir);
|
||||
|
||||
var downloadFileaction = $(context.$file).find('.fileactions .action-download');
|
||||
|
||||
// don't allow a second click on the download action
|
||||
if(downloadFileaction.hasClass('disabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (url) {
|
||||
var disableLoadingState = function() {
|
||||
context.fileList.showFileBusyState(filename, false);
|
||||
};
|
||||
|
||||
context.fileList.showFileBusyState(filename, true);
|
||||
OCA.Files.Files.handleDownload(url, disableLoadingState);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.registerAction({
|
||||
name: 'Rename',
|
||||
displayName: t('files', 'Rename'),
|
||||
mime: 'all',
|
||||
order: -30,
|
||||
permissions: OC.PERMISSION_UPDATE,
|
||||
iconClass: 'icon-rename',
|
||||
actionHandler: function (filename, context) {
|
||||
context.fileList.rename(filename);
|
||||
}
|
||||
});
|
||||
|
||||
this.registerAction({
|
||||
name: 'MoveCopy',
|
||||
displayName: function(context) {
|
||||
var permissions = context.fileInfoModel.attributes.permissions;
|
||||
if (permissions & OC.PERMISSION_UPDATE) {
|
||||
if (!context.fileInfoModel.canDownload()) {
|
||||
return t('files', 'Move');
|
||||
}
|
||||
return t('files', 'Move or copy');
|
||||
}
|
||||
return t('files', 'Copy');
|
||||
},
|
||||
mime: 'all',
|
||||
order: -25,
|
||||
permissions: $('#isPublic').val() ? OC.PERMISSION_UPDATE : OC.PERMISSION_READ,
|
||||
iconClass: 'icon-external',
|
||||
actionHandler: function (filename, context) {
|
||||
var permissions = context.fileInfoModel.attributes.permissions;
|
||||
var actions = OC.dialogs.FILEPICKER_TYPE_COPY;
|
||||
if (permissions & OC.PERMISSION_UPDATE) {
|
||||
if (!context.fileInfoModel.canDownload()) {
|
||||
actions = OC.dialogs.FILEPICKER_TYPE_MOVE;
|
||||
} else {
|
||||
actions = OC.dialogs.FILEPICKER_TYPE_COPY_MOVE;
|
||||
}
|
||||
}
|
||||
var dialogDir = context.dir;
|
||||
if (typeof context.fileList.dirInfo.dirLastCopiedTo !== 'undefined') {
|
||||
dialogDir = context.fileList.dirInfo.dirLastCopiedTo;
|
||||
}
|
||||
OC.dialogs.filepicker(t('files', 'Choose target folder'), function(targetPath, type) {
|
||||
if (type === OC.dialogs.FILEPICKER_TYPE_COPY) {
|
||||
context.fileList.copy(filename, targetPath, false, context.dir);
|
||||
}
|
||||
if (type === OC.dialogs.FILEPICKER_TYPE_MOVE) {
|
||||
context.fileList.move(filename, targetPath, false, context.dir);
|
||||
}
|
||||
context.fileList.dirInfo.dirLastCopiedTo = targetPath;
|
||||
}, false, "httpd/unix-directory", true, actions, dialogDir);
|
||||
}
|
||||
});
|
||||
|
||||
if (Boolean(OC.appswebroots.files_reminders) && Boolean(OC.appswebroots.notifications)) {
|
||||
this.registerAction({
|
||||
name: 'SetReminder',
|
||||
displayName: function(_context) {
|
||||
return t('files', 'Set reminder');
|
||||
},
|
||||
mime: 'all',
|
||||
order: -24,
|
||||
icon: function(_filename, _context) {
|
||||
return OC.imagePath('files_reminders', 'alarm.svg')
|
||||
},
|
||||
permissions: $('#isPublic').val() ? null : OC.PERMISSION_READ,
|
||||
actionHandler: function(_filename, _context) {},
|
||||
});
|
||||
}
|
||||
|
||||
if (!/Android|iPhone|iPad|iPod/i.test(navigator.userAgent) && !!window.oc_current_user) {
|
||||
this.registerAction({
|
||||
name: 'EditLocally',
|
||||
displayName: function(context) {
|
||||
var locked = context.$file.data('locked');
|
||||
if (!locked) {
|
||||
return t('files', 'Edit locally');
|
||||
}
|
||||
},
|
||||
mime: 'all',
|
||||
order: -23,
|
||||
icon: function(filename, context) {
|
||||
var locked = context.$file.data('locked');
|
||||
if (!locked) {
|
||||
return OC.imagePath('files', 'computer.svg')
|
||||
}
|
||||
},
|
||||
permissions: OC.PERMISSION_UPDATE,
|
||||
actionHandler: function (filename, context) {
|
||||
var dir = context.dir || context.fileList.getCurrentDirectory();
|
||||
var path = dir === '/' ? dir + filename : dir + '/' + filename;
|
||||
context.fileList.openLocalClient(path);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
this.registerAction({
|
||||
name: 'Open',
|
||||
mime: 'dir',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: '',
|
||||
actionHandler: function (filename, context) {
|
||||
let dir, id
|
||||
if (context.$file) {
|
||||
dir = context.$file.attr('data-path')
|
||||
id = context.$file.attr('data-id')
|
||||
} else {
|
||||
dir = context.fileList.getCurrentDirectory()
|
||||
id = context.fileId
|
||||
}
|
||||
if (OCA.Files.App && OCA.Files.App.getActiveView() !== 'files') {
|
||||
OCA.Files.App.setActiveView('files', {silent: true});
|
||||
OCA.Files.App.fileList.changeDirectory(OC.joinPaths(dir, filename), true, true);
|
||||
} else {
|
||||
context.fileList.changeDirectory(OC.joinPaths(dir, filename), true, false, parseInt(id, 10));
|
||||
}
|
||||
},
|
||||
displayName: t('files', 'Open')
|
||||
});
|
||||
|
||||
this.registerAction({
|
||||
name: 'Delete',
|
||||
displayName: function(context) {
|
||||
var mountType = context.$file.attr('data-mounttype');
|
||||
var type = context.$file.attr('data-type');
|
||||
var deleteTitle = (type && type === 'file')
|
||||
? t('files', 'Delete file')
|
||||
: t('files', 'Delete folder')
|
||||
if (mountType === 'external-root') {
|
||||
deleteTitle = t('files', 'Disconnect storage');
|
||||
} else if (mountType === 'shared-root') {
|
||||
deleteTitle = t('files', 'Leave this share');
|
||||
}
|
||||
return deleteTitle;
|
||||
},
|
||||
mime: 'all',
|
||||
order: 1000,
|
||||
// permission is READ because we show a hint instead if there is no permission
|
||||
permissions: OC.PERMISSION_DELETE,
|
||||
iconClass: 'icon-delete',
|
||||
actionHandler: function(fileName, context) {
|
||||
// if there is no permission to delete do nothing
|
||||
if((context.$file.data('permissions') & OC.PERMISSION_DELETE) === 0) {
|
||||
return;
|
||||
}
|
||||
context.fileList.do_delete(fileName, context.dir);
|
||||
$('.tipsy').remove();
|
||||
|
||||
// close sidebar on delete
|
||||
const path = context.dir + '/' + fileName
|
||||
if (OCA.Files.Sidebar && OCA.Files.Sidebar.file === path) {
|
||||
OCA.Files.Sidebar.close()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.setDefault('dir', 'Open');
|
||||
}
|
||||
};
|
||||
|
||||
OCA.Files.FileActions = FileActions;
|
||||
|
||||
/**
|
||||
* Replaces the button icon with a loading spinner and vice versa
|
||||
* - also adds the class disabled to the passed in element
|
||||
*
|
||||
* @param {jQuery} $buttonElement The button element
|
||||
* @param {boolean} showIt whether to show the spinner(true) or to hide it(false)
|
||||
*/
|
||||
OCA.Files.FileActions.updateFileActionSpinner = function($buttonElement, showIt) {
|
||||
var $icon = $buttonElement.find('.icon');
|
||||
if (showIt) {
|
||||
var $loadingIcon = $('<span class="icon icon-loading-small"></span>');
|
||||
$icon.after($loadingIcon);
|
||||
$icon.addClass('hidden');
|
||||
} else {
|
||||
$buttonElement.find('.icon-loading-small').remove();
|
||||
$buttonElement.find('.icon').removeClass('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* File action attributes.
|
||||
*
|
||||
* @todo make this a real class in the future
|
||||
* @typedef {Object} OCA.Files.FileAction
|
||||
*
|
||||
* @property {String} name identifier of the action
|
||||
* @property {(String|OCA.Files.FileActions~displayNameFunction)} displayName
|
||||
* display name string for the action, or function that returns the display name.
|
||||
* Defaults to the name given in name property
|
||||
* @property {String} mime mime type
|
||||
* @property {String} filename filename
|
||||
* @property {number} permissions permissions
|
||||
* @property {(Function|String)} icon icon path to the icon or function that returns it (deprecated, use iconClass instead)
|
||||
* @property {(String|OCA.Files.FileActions~iconClassFunction)} iconClass class name of the icon (recommended for theming)
|
||||
* @property {OCA.Files.FileActions~renderActionFunction} [render] optional rendering function
|
||||
* @property {OCA.Files.FileActions~actionHandler} actionHandler action handler function
|
||||
*/
|
||||
|
||||
/**
|
||||
* File action context attributes.
|
||||
*
|
||||
* @typedef {Object} OCA.Files.FileActionContext
|
||||
*
|
||||
* @property {Object} $file jQuery file row element
|
||||
* @property {OCA.Files.FileActions} fileActions file actions object
|
||||
* @property {OCA.Files.FileList} fileList file list object
|
||||
*/
|
||||
|
||||
/**
|
||||
* Render function for actions.
|
||||
* The function must render a link element somewhere in the DOM
|
||||
* and return it. The function should NOT register the event handler
|
||||
* as this will be done after the link was returned.
|
||||
*
|
||||
* @callback OCA.Files.FileActions~renderActionFunction
|
||||
* @param {OCA.Files.FileAction} actionSpec action definition
|
||||
* @param {Object} $row row container
|
||||
* @param {boolean} isDefault true if the action is the default one,
|
||||
* false otherwise
|
||||
* @return {Object} jQuery link object
|
||||
*/
|
||||
|
||||
/**
|
||||
* Display name function for actions.
|
||||
* The function returns the display name of the action using
|
||||
* the given context information..
|
||||
*
|
||||
* @callback OCA.Files.FileActions~displayNameFunction
|
||||
* @param {OCA.Files.FileActionContext} context action context
|
||||
* @return {String} display name
|
||||
*/
|
||||
|
||||
/**
|
||||
* Icon class function for actions.
|
||||
* The function returns the icon class of the action using
|
||||
* the given context information.
|
||||
*
|
||||
* @callback OCA.Files.FileActions~iconClassFunction
|
||||
* @param {String} fileName name of the file on which the action must be performed
|
||||
* @param {OCA.Files.FileActionContext} context action context
|
||||
* @return {String} icon class
|
||||
*/
|
||||
|
||||
/**
|
||||
* Action handler function for file actions
|
||||
*
|
||||
* @callback OCA.Files.FileActions~actionHandler
|
||||
* @param {String} fileName name of the file on which the action must be performed
|
||||
* @param context context
|
||||
* @param {String} context.dir directory of the file
|
||||
* @param {OCA.Files.FileInfoModel} fileInfoModel file info model
|
||||
* @param {Object} [context.$file] jQuery element of the file [DEPRECATED]
|
||||
* @param {OCA.Files.FileList} [context.fileList] the FileList instance on which the action occurred [DEPRECATED]
|
||||
* @param {OCA.Files.FileActions} context.fileActions the FileActions instance on which the action occurred
|
||||
*/
|
||||
|
||||
// global file actions to be used by all lists
|
||||
OCA.Files.fileActions = new OCA.Files.FileActions();
|
||||
})();
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* Construct a new FileActionsMenu instance
|
||||
* @constructs FileActionsMenu
|
||||
* @memberof OCA.Files
|
||||
*/
|
||||
var FileActionsMenu = OC.Backbone.View.extend({
|
||||
tagName: 'div',
|
||||
className: 'fileActionsMenu popovermenu bubble hidden open menu',
|
||||
|
||||
/**
|
||||
* Current context
|
||||
*
|
||||
* @type OCA.Files.FileActionContext
|
||||
*/
|
||||
_context: null,
|
||||
|
||||
events: {
|
||||
'click a.action': '_onClickAction'
|
||||
},
|
||||
|
||||
template: function(data) {
|
||||
return OCA.Files.Templates['fileactionsmenu'](data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler whenever an action has been clicked within the menu
|
||||
*
|
||||
* @param {Object} event event object
|
||||
*/
|
||||
_onClickAction: function(event) {
|
||||
var $target = $(event.target);
|
||||
if (!$target.is('a')) {
|
||||
$target = $target.closest('a');
|
||||
}
|
||||
var fileActions = this._context.fileActions;
|
||||
var actionName = $target.attr('data-action');
|
||||
var actions = fileActions.getActions(
|
||||
fileActions.getCurrentMimeType(),
|
||||
fileActions.getCurrentType(),
|
||||
fileActions.getCurrentPermissions(),
|
||||
fileActions.getCurrentFile()
|
||||
);
|
||||
var actionSpec = actions[actionName];
|
||||
var fileName = this._context.$file.attr('data-file');
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
OC.hideMenus();
|
||||
|
||||
actionSpec.action(
|
||||
fileName,
|
||||
this._context
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the menu with the currently set items
|
||||
*/
|
||||
render: function() {
|
||||
var self = this;
|
||||
var fileActions = this._context.fileActions;
|
||||
var actions = fileActions.getActions(
|
||||
fileActions.getCurrentMimeType(),
|
||||
fileActions.getCurrentType(),
|
||||
fileActions.getCurrentPermissions(),
|
||||
fileActions.getCurrentFile()
|
||||
);
|
||||
|
||||
var defaultAction = fileActions.getCurrentDefaultFileAction();
|
||||
|
||||
var items = _.filter(actions, function(actionSpec) {
|
||||
return !defaultAction || actionSpec.name !== defaultAction.name;
|
||||
});
|
||||
items = _.map(items, function(item) {
|
||||
if (_.isFunction(item.displayName)) {
|
||||
item = _.extend({}, item);
|
||||
item.displayName = item.displayName(self._context);
|
||||
}
|
||||
if (_.isFunction(item.iconClass)) {
|
||||
var fileName = self._context.$file.attr('data-file');
|
||||
item = _.extend({}, item);
|
||||
item.iconClass = item.iconClass(fileName, self._context);
|
||||
}
|
||||
if (_.isFunction(item.icon)) {
|
||||
var fileName = self._context.$file.attr('data-file');
|
||||
item = _.extend({}, item);
|
||||
item.icon = item.icon(fileName, self._context);
|
||||
}
|
||||
item.inline = item.type === OCA.Files.FileActions.TYPE_INLINE
|
||||
return item;
|
||||
});
|
||||
items = items.sort(function(actionA, actionB) {
|
||||
var orderA = actionA.order || 0;
|
||||
var orderB = actionB.order || 0;
|
||||
if (orderB === orderA) {
|
||||
return OC.Util.naturalSortCompare(actionA.displayName, actionB.displayName);
|
||||
}
|
||||
return orderA - orderB;
|
||||
});
|
||||
|
||||
items = _.map(items, function(item) {
|
||||
item.nameLowerCase = item.name.toLowerCase();
|
||||
return item;
|
||||
});
|
||||
|
||||
this.$el.html(this.template({
|
||||
items: items
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays the menu under the given element
|
||||
*
|
||||
* @param {OCA.Files.FileActionContext} context context
|
||||
* @param {Object} $trigger trigger element
|
||||
*/
|
||||
show: function(context) {
|
||||
this._context = context;
|
||||
|
||||
this.render();
|
||||
this.$el.removeClass('hidden');
|
||||
|
||||
window._nc_event_bus.emit('files:action-menu:opened', {
|
||||
el: this.$el[0],
|
||||
context,
|
||||
})
|
||||
|
||||
OC.showMenu(null, this.$el);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.Files.FileActionsMenu = FileActionsMenu;
|
||||
|
||||
})();
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function(OC, OCA) {
|
||||
|
||||
/**
|
||||
* @class OC.Files.FileInfo
|
||||
* @classdesc File information
|
||||
*
|
||||
* @param {Object} attributes file data
|
||||
* @param {number} attributes.id file id
|
||||
* @param {string} attributes.name file name
|
||||
* @param {string} attributes.path path leading to the file,
|
||||
* without the file name and with a leading slash
|
||||
* @param {number} attributes.size size
|
||||
* @param {string} attributes.mimetype mime type
|
||||
* @param {string} attributes.icon icon URL
|
||||
* @param {number} attributes.permissions permissions
|
||||
* @param {Date} attributes.mtime modification time
|
||||
* @param {string} attributes.etag etag
|
||||
* @param {string} mountType mount type
|
||||
*
|
||||
* @since 8.2
|
||||
*/
|
||||
var FileInfoModel = OC.Backbone.Model.extend({
|
||||
|
||||
defaults: {
|
||||
mimetype: 'application/octet-stream',
|
||||
path: ''
|
||||
},
|
||||
|
||||
_filesClient: null,
|
||||
|
||||
initialize: function(data, options) {
|
||||
if (!_.isUndefined(data.id)) {
|
||||
data.id = parseInt(data.id, 10);
|
||||
}
|
||||
|
||||
if( options ){
|
||||
if (options.filesClient) {
|
||||
this._filesClient = options.filesClient;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether this file is a directory
|
||||
*
|
||||
* @return {boolean} true if this is a directory, false otherwise
|
||||
*/
|
||||
isDirectory: function() {
|
||||
return this.get('mimetype') === 'httpd/unix-directory';
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether this file is an image
|
||||
*
|
||||
* @return {boolean} true if this is an image, false otherwise
|
||||
*/
|
||||
isImage: function() {
|
||||
if (!this.has('mimetype')) {
|
||||
return false;
|
||||
}
|
||||
return this.get('mimetype').substr(0, 6) === 'image/'
|
||||
|| this.get('mimetype') === 'application/postscript'
|
||||
|| this.get('mimetype') === 'application/illustrator'
|
||||
|| this.get('mimetype') === 'application/x-photoshop';
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the full path to this file
|
||||
*
|
||||
* @return {string} full path
|
||||
*/
|
||||
getFullPath: function() {
|
||||
return OC.joinPaths(this.get('path'), this.get('name'));
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the mimetype of the file
|
||||
*
|
||||
* @return {string} mimetype
|
||||
*/
|
||||
getMimeType: function() {
|
||||
return this.get('mimetype');
|
||||
},
|
||||
|
||||
/**
|
||||
* Reloads missing properties from server and set them in the model.
|
||||
* @param properties array of properties to be reloaded
|
||||
* @return ajax call object
|
||||
*/
|
||||
reloadProperties: function(properties) {
|
||||
if( !this._filesClient ){
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var deferred = $.Deferred();
|
||||
|
||||
var targetPath = OC.joinPaths(this.get('path') + '/', this.get('name'));
|
||||
|
||||
this._filesClient.getFileInfo(targetPath, {
|
||||
properties: properties
|
||||
})
|
||||
.then(function(status, data) {
|
||||
// the following lines should be extracted to a mapper
|
||||
|
||||
if( properties.indexOf(OC.Files.Client.PROPERTY_GETCONTENTLENGTH) !== -1
|
||||
|| properties.indexOf(OC.Files.Client.PROPERTY_SIZE) !== -1 ) {
|
||||
self.set('size', data.size);
|
||||
}
|
||||
|
||||
deferred.resolve(status, data);
|
||||
})
|
||||
.fail(function(status) {
|
||||
OC.Notification.show(t('files', 'Could not load info for file "{file}"', {file: self.get('name')}), {type: 'error'});
|
||||
deferred.reject(status);
|
||||
});
|
||||
|
||||
return deferred.promise();
|
||||
},
|
||||
|
||||
canDownload: function() {
|
||||
for (const i in this.attributes.shareAttributes) {
|
||||
const attr = this.attributes.shareAttributes[i]
|
||||
if (attr.scope === 'permissions' && attr.key === 'download') {
|
||||
return attr.value === true
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
});
|
||||
|
||||
if (!OCA.Files) {
|
||||
OCA.Files = {};
|
||||
}
|
||||
OCA.Files.FileInfoModel = FileInfoModel;
|
||||
|
||||
})(OC, OCA);
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,92 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var FileMultiSelectMenu = OC.Backbone.View.extend({
|
||||
tagName: 'div',
|
||||
className: 'filesSelectMenu popovermenu bubble menu-center',
|
||||
_scopes: null,
|
||||
initialize: function(menuItems) {
|
||||
this._scopes = menuItems;
|
||||
},
|
||||
events: {
|
||||
'click a.action': '_onClickAction'
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the menu with the currently set items
|
||||
*/
|
||||
render: function() {
|
||||
this.$el.html(OCA.Files.Templates['filemultiselectmenu']({
|
||||
items: this._scopes
|
||||
}));
|
||||
},
|
||||
/**
|
||||
* Displays the menu under the given element
|
||||
*
|
||||
* @param {OCA.Files.FileActionContext} context context
|
||||
* @param {Object} $trigger trigger element
|
||||
*/
|
||||
show: function(context) {
|
||||
this._context = context;
|
||||
this.$el.removeClass('hidden');
|
||||
if (window.innerWidth < 480) {
|
||||
this.$el.removeClass('menu-center').addClass('menu-right');
|
||||
} else {
|
||||
this.$el.removeClass('menu-right').addClass('menu-center');
|
||||
}
|
||||
OC.showMenu(null, this.$el);
|
||||
return false;
|
||||
},
|
||||
toggleItemVisibility: function (itemName, show) {
|
||||
if (show) {
|
||||
this.$el.find('.item-' + itemName).removeClass('hidden');
|
||||
} else {
|
||||
this.$el.find('.item-' + itemName).addClass('hidden');
|
||||
}
|
||||
},
|
||||
updateItemText: function (itemName, translation) {
|
||||
this.$el.find('.item-' + itemName).find('.label').text(translation);
|
||||
},
|
||||
toggleLoading: function (itemName, showLoading) {
|
||||
var $actionElement = this.$el.find('.item-' + itemName);
|
||||
if ($actionElement.length === 0) {
|
||||
return;
|
||||
}
|
||||
var $icon = $actionElement.find('.icon');
|
||||
if (showLoading) {
|
||||
var $loadingIcon = $('<span class="icon icon-loading-small"></span>');
|
||||
$icon.after($loadingIcon);
|
||||
$icon.addClass('hidden');
|
||||
$actionElement.addClass('disabled');
|
||||
} else {
|
||||
$actionElement.find('.icon-loading-small').remove();
|
||||
$actionElement.find('.icon').removeClass('hidden');
|
||||
$actionElement.removeClass('disabled');
|
||||
}
|
||||
},
|
||||
isDisabled: function (itemName) {
|
||||
var $actionElement = this.$el.find('.item-' + itemName);
|
||||
return $actionElement.hasClass('disabled');
|
||||
},
|
||||
/**
|
||||
* Event handler whenever an action has been clicked within the menu
|
||||
*
|
||||
* @param {Object} event event object
|
||||
*/
|
||||
_onClickAction: function (event) {
|
||||
var $target = $(event.currentTarget);
|
||||
if (!$target.hasClass('menuitem')) {
|
||||
$target = $target.closest('.menuitem');
|
||||
}
|
||||
|
||||
OC.hideMenus();
|
||||
this._context.multiSelectMenuClick(event, $target.data('action'));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.Files.FileMultiSelectMenu = FileMultiSelectMenu;
|
||||
})(OC, OCA);
|
||||
|
|
@ -1,543 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2012-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/* global getURLParameter */
|
||||
/**
|
||||
* Utility class for file related operations
|
||||
*/
|
||||
(function() {
|
||||
var Files = {
|
||||
// file space size sync
|
||||
_updateStorageStatistics: function(currentDir) {
|
||||
var state = Files.updateStorageStatistics;
|
||||
if (state.dir){
|
||||
if (state.dir === currentDir) {
|
||||
return;
|
||||
}
|
||||
// cancel previous call, as it was for another dir
|
||||
state.call.abort();
|
||||
}
|
||||
state.dir = currentDir;
|
||||
state.call = $.getJSON(OC.generateUrl('apps/files/api/v1/stats?dir={dir}', {
|
||||
dir: currentDir,
|
||||
}), function(response) {
|
||||
state.dir = null;
|
||||
state.call = null;
|
||||
Files.updateMaxUploadFilesize(response);
|
||||
});
|
||||
},
|
||||
// update quota
|
||||
updateStorageQuotas: function() {
|
||||
Files._updateStorageQuotasThrottled();
|
||||
},
|
||||
_updateStorageQuotas: function() {
|
||||
var state = Files.updateStorageQuotas;
|
||||
state.call = $.getJSON(OC.generateUrl('apps/files/api/v1/stats'), function(response) {
|
||||
Files.updateQuota(response);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Update storage statistics such as free space, max upload,
|
||||
* etc based on the given directory.
|
||||
*
|
||||
* Note this function is debounced to avoid making too
|
||||
* many ajax calls in a row.
|
||||
*
|
||||
* @param dir directory
|
||||
* @param force whether to force retrieving
|
||||
*/
|
||||
updateStorageStatistics: function(dir, force) {
|
||||
if (!OC.currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (force) {
|
||||
Files._updateStorageStatistics(dir);
|
||||
}
|
||||
else {
|
||||
Files._updateStorageStatisticsDebounced(dir);
|
||||
}
|
||||
},
|
||||
|
||||
updateMaxUploadFilesize:function(response) {
|
||||
if (response === undefined) {
|
||||
return;
|
||||
}
|
||||
if (response.data !== undefined && response.data.uploadMaxFilesize !== undefined) {
|
||||
$('#free_space').val(response.data.freeSpace);
|
||||
$('#upload.button').attr('title', response.data.maxHumanFilesize);
|
||||
$('#usedSpacePercent').val(response.data.usedSpacePercent);
|
||||
$('#usedSpacePercent').data('mount-type', response.data.mountType);
|
||||
$('#usedSpacePercent').data('mount-point', response.data.mountPoint);
|
||||
$('#owner').val(response.data.owner);
|
||||
$('#ownerDisplayName').val(response.data.ownerDisplayName);
|
||||
Files.displayStorageWarnings();
|
||||
OCA.Files.App.fileList._updateDirectoryPermissions();
|
||||
}
|
||||
if (response[0] === undefined) {
|
||||
return;
|
||||
}
|
||||
if (response[0].uploadMaxFilesize !== undefined) {
|
||||
$('#upload.button').attr('title', response[0].maxHumanFilesize);
|
||||
$('#usedSpacePercent').val(response[0].usedSpacePercent);
|
||||
Files.displayStorageWarnings();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
updateQuota:function(response) {
|
||||
if (response === undefined) {
|
||||
return;
|
||||
}
|
||||
if (response.data !== undefined
|
||||
&& response.data.quota !== undefined
|
||||
&& response.data.total !== undefined
|
||||
&& response.data.used !== undefined
|
||||
&& response.data.usedSpacePercent !== undefined) {
|
||||
var humanUsed = OC.Util.humanFileSize(response.data.used, true, false);
|
||||
var humanTotal = OC.Util.humanFileSize(response.data.total, true, false);
|
||||
if (response.data.quota > 0) {
|
||||
$('#quota').attr('title', t('files', '{used}%', {used: Math.round(response.data.usedSpacePercent)}));
|
||||
$('#quota progress').val(response.data.usedSpacePercent);
|
||||
$('#quotatext').html(t('files', '{used} of {quota} used', {used: humanUsed, quota: humanTotal}));
|
||||
} else {
|
||||
$('#quotatext').html(t('files', '{used} used', {used: humanUsed}));
|
||||
}
|
||||
if (response.data.usedSpacePercent > 80) {
|
||||
$('#quota progress').addClass('warn');
|
||||
} else {
|
||||
$('#quota progress').removeClass('warn');
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Fix path name by removing double slash at the beginning, if any
|
||||
*/
|
||||
fixPath: function(fileName) {
|
||||
if (fileName.substr(0, 2) == '//') {
|
||||
return fileName.substr(1);
|
||||
}
|
||||
return fileName;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether the given file name is valid.
|
||||
* @param name file name to check
|
||||
* @return true if the file name is valid.
|
||||
* Throws a string exception with an error message if
|
||||
* the file name is not valid
|
||||
*
|
||||
* NOTE: This function is duplicated in the filepicker inside core/src/OC/dialogs.js
|
||||
*/
|
||||
isFileNameValid: function (name) {
|
||||
var trimmedName = name.trim();
|
||||
if (trimmedName === '.' || trimmedName === '..')
|
||||
{
|
||||
throw t('files', '"{name}" is an invalid file name.', {name: name});
|
||||
} else if (trimmedName.length === 0) {
|
||||
throw t('files', 'File name cannot be empty.');
|
||||
} else if (trimmedName.indexOf('/') !== -1) {
|
||||
throw t('files', '"/" is not allowed inside a file name.');
|
||||
} else if (!!(trimmedName.match(OC.config.blacklist_files_regex))) {
|
||||
throw t('files', '"{name}" is not an allowed filetype', {name: name});
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
displayStorageWarnings: function() {
|
||||
if (!OC.Notification.isHidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var usedSpacePercent = $('#usedSpacePercent').val(),
|
||||
owner = $('#owner').val(),
|
||||
ownerDisplayName = $('#ownerDisplayName').val(),
|
||||
mountType = $('#usedSpacePercent').data('mount-type'),
|
||||
mountPoint = $('#usedSpacePercent').data('mount-point');
|
||||
if (usedSpacePercent > 98) {
|
||||
if (owner !== OC.getCurrentUser().uid) {
|
||||
OC.Notification.show(t('files', 'Storage of {owner} is full, files cannot be updated or synced anymore!',
|
||||
{owner: ownerDisplayName}), {type: 'error'}
|
||||
);
|
||||
} else if (mountType === 'group') {
|
||||
OC.Notification.show(t('files',
|
||||
'Group folder "{mountPoint}" is full, files cannot be updated or synced anymore!',
|
||||
{mountPoint: mountPoint}),
|
||||
{type: 'error'}
|
||||
);
|
||||
} else if (mountType === 'external') {
|
||||
OC.Notification.show(t('files',
|
||||
'External storage "{mountPoint}" is full, files cannot be updated or synced anymore!',
|
||||
{mountPoint: mountPoint}),
|
||||
{type : 'error'}
|
||||
);
|
||||
} else {
|
||||
OC.Notification.show(t('files',
|
||||
'Your storage is full, files cannot be updated or synced anymore!'),
|
||||
{type: 'error'}
|
||||
);
|
||||
}
|
||||
} else if (usedSpacePercent > 90) {
|
||||
if (owner !== OC.getCurrentUser().uid) {
|
||||
OC.Notification.show(t('files', 'Storage of {owner} is almost full ({usedSpacePercent}%).',
|
||||
{
|
||||
usedSpacePercent: usedSpacePercent,
|
||||
owner: ownerDisplayName
|
||||
}),
|
||||
{
|
||||
type: 'error'
|
||||
}
|
||||
);
|
||||
} else if (mountType === 'group') {
|
||||
OC.Notification.show(t('files',
|
||||
'Group folder "{mountPoint}" is almost full ({usedSpacePercent}%).',
|
||||
{mountPoint: mountPoint, usedSpacePercent: usedSpacePercent}),
|
||||
{type : 'error'}
|
||||
);
|
||||
} else if (mountType === 'external') {
|
||||
OC.Notification.show(t('files',
|
||||
'External storage "{mountPoint}" is almost full ({usedSpacePercent}%).',
|
||||
{mountPoint: mountPoint, usedSpacePercent: usedSpacePercent}),
|
||||
{type : 'error'}
|
||||
);
|
||||
} else {
|
||||
OC.Notification.show(t('files', 'Your storage is almost full ({usedSpacePercent}%).',
|
||||
{usedSpacePercent: usedSpacePercent}),
|
||||
{type : 'error'}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the download URL of the given file(s)
|
||||
* @param {string} filename string or array of file names to download
|
||||
* @param {string} [dir] optional directory in which the file name is, defaults to the current directory
|
||||
* @param {boolean} [isDir=false] whether the given filename is a directory and might need a special URL
|
||||
*/
|
||||
getDownloadUrl: function(filename, dir, isDir) {
|
||||
if (!_.isArray(filename) && !isDir) {
|
||||
var pathSections = dir.split('/');
|
||||
pathSections.push(filename);
|
||||
var encodedPath = '';
|
||||
_.each(pathSections, function(section) {
|
||||
if (section !== '') {
|
||||
encodedPath += '/' + encodeURIComponent(section);
|
||||
}
|
||||
});
|
||||
return OC.linkToRemoteBase('webdav') + encodedPath;
|
||||
}
|
||||
|
||||
if (_.isArray(filename)) {
|
||||
filename = JSON.stringify(filename);
|
||||
}
|
||||
|
||||
var params = {
|
||||
dir: dir,
|
||||
files: filename
|
||||
};
|
||||
return this.getAjaxUrl('download', params);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the ajax URL for a given action
|
||||
* @param action action string
|
||||
* @param params optional params map
|
||||
*/
|
||||
getAjaxUrl: function(action, params) {
|
||||
var q = '';
|
||||
if (params) {
|
||||
q = '?' + OC.buildQueryString(params);
|
||||
}
|
||||
return OC.filePath('files', 'ajax', action + '.php') + q;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch the icon url for the mimetype
|
||||
* @param {string} mime The mimetype
|
||||
* @param {Files~mimeicon} ready Function to call when mimetype is retrieved
|
||||
* @deprecated use OC.MimeType.getIconUrl(mime)
|
||||
*/
|
||||
getMimeIcon: function(mime, ready) {
|
||||
ready(OC.MimeType.getIconUrl(mime));
|
||||
},
|
||||
|
||||
/**
|
||||
* Generates a preview URL based on the URL space.
|
||||
* @param urlSpec attributes for the URL
|
||||
* @param {number} urlSpec.x width
|
||||
* @param {number} urlSpec.y height
|
||||
* @param {String} urlSpec.file path to the file
|
||||
* @return preview URL
|
||||
* @deprecated used OCA.Files.FileList.generatePreviewUrl instead
|
||||
*/
|
||||
generatePreviewUrl: function(urlSpec) {
|
||||
OC.debug && console.warn('DEPRECATED: please use generatePreviewUrl() from an OCA.Files.FileList instance');
|
||||
return OCA.Files.App.fileList.generatePreviewUrl(urlSpec);
|
||||
},
|
||||
|
||||
/**
|
||||
* Lazy load preview
|
||||
* @deprecated used OCA.Files.FileList.lazyLoadPreview instead
|
||||
*/
|
||||
lazyLoadPreview : function(path, mime, ready, width, height, etag) {
|
||||
OC.debug && console.warn('DEPRECATED: please use lazyLoadPreview() from an OCA.Files.FileList instance');
|
||||
return FileList.lazyLoadPreview({
|
||||
path: path,
|
||||
mime: mime,
|
||||
callback: ready,
|
||||
width: width,
|
||||
height: height,
|
||||
etag: etag
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the files view
|
||||
*/
|
||||
initialize: function() {
|
||||
Files.bindKeyboardShortcuts(document, $);
|
||||
|
||||
// drag&drop support using jquery.fileupload
|
||||
// TODO use OC.dialogs
|
||||
$(document).bind('drop dragover', function (e) {
|
||||
e.preventDefault(); // prevent browser from doing anything, if file isn't dropped in dropZone
|
||||
});
|
||||
|
||||
// display storage warnings
|
||||
setTimeout(Files.displayStorageWarnings, 100);
|
||||
|
||||
// only possible at the moment if user is logged in or the files app is loaded
|
||||
if (OC.currentUser && OCA.Files.App && OC.config.session_keepalive) {
|
||||
// start on load - we ask the server every 5 minutes
|
||||
var func = _.bind(OCA.Files.App.fileList.updateStorageStatistics, OCA.Files.App.fileList);
|
||||
var updateStorageStatisticsInterval = 5*60*1000;
|
||||
var updateStorageStatisticsIntervalId = setInterval(func, updateStorageStatisticsInterval);
|
||||
|
||||
// TODO: this should also stop when switching to another view
|
||||
// Use jquery-visibility to de-/re-activate file stats sync
|
||||
if ($.support.pageVisibility) {
|
||||
$(document).on({
|
||||
'show': function() {
|
||||
if (!updateStorageStatisticsIntervalId) {
|
||||
updateStorageStatisticsIntervalId = setInterval(func, updateStorageStatisticsInterval);
|
||||
}
|
||||
},
|
||||
'hide': function() {
|
||||
clearInterval(updateStorageStatisticsIntervalId);
|
||||
updateStorageStatisticsIntervalId = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$('#webdavurl').on('click touchstart', function () {
|
||||
this.focus();
|
||||
this.setSelectionRange(0, this.value.length);
|
||||
});
|
||||
|
||||
//FIXME scroll to and highlight preselected file
|
||||
/*
|
||||
if (getURLParameter('scrollto')) {
|
||||
FileList.scrollTo(getURLParameter('scrollto'));
|
||||
}
|
||||
*/
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the download and calls the callback function once the download has started
|
||||
* - browser sends download request and adds parameter with a token
|
||||
* - server notices this token and adds a set cookie to the download response
|
||||
* - browser now adds this cookie for the domain
|
||||
* - JS periodically checks for this cookie and then knows when the download has started to call the callback
|
||||
*
|
||||
* @param {string} url download URL
|
||||
* @param {Function} callback function to call once the download has started
|
||||
*/
|
||||
handleDownload: function(url, callback) {
|
||||
var randomToken = Math.random().toString(36).substring(2),
|
||||
checkForDownloadCookie = function() {
|
||||
if (!OC.Util.isCookieSetToValue('ocDownloadStarted', randomToken)){
|
||||
return false;
|
||||
} else {
|
||||
callback();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if (url.indexOf('?') >= 0) {
|
||||
url += '&';
|
||||
} else {
|
||||
url += '?';
|
||||
}
|
||||
OC.redirect(url + 'downloadStartSecret=' + randomToken);
|
||||
OC.Util.waitFor(checkForDownloadCookie, 500);
|
||||
}
|
||||
};
|
||||
|
||||
Files._updateStorageStatisticsDebounced = _.debounce(Files._updateStorageStatistics, 250);
|
||||
Files._updateStorageQuotasThrottled = _.throttle(Files._updateStorageQuotas, 30000);
|
||||
OCA.Files.Files = Files;
|
||||
})();
|
||||
|
||||
// TODO: move to FileList
|
||||
var createDragShadow = function(event) {
|
||||
// FIXME: inject file list instance somehow
|
||||
/* global FileList, Files */
|
||||
|
||||
//select dragged file
|
||||
var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked');
|
||||
if (!isDragSelected) {
|
||||
//select dragged file
|
||||
FileList._selectFileEl($(event.target).parents('tr:first'), true, false);
|
||||
}
|
||||
|
||||
// do not show drag shadow for too many files
|
||||
var selectedFiles = _.first(FileList.getSelectedFiles(), FileList.pageSize());
|
||||
selectedFiles = _.sortBy(selectedFiles, FileList._fileInfoCompare);
|
||||
|
||||
if (!isDragSelected && selectedFiles.length === 1) {
|
||||
//revert the selection
|
||||
FileList._selectFileEl($(event.target).parents('tr:first'), false, false);
|
||||
}
|
||||
|
||||
// build dragshadow
|
||||
var dragshadow = $('<table class="dragshadow"></table>');
|
||||
var tbody = $('<tbody></tbody>');
|
||||
dragshadow.append(tbody);
|
||||
|
||||
var dir = FileList.getCurrentDirectory();
|
||||
|
||||
$(selectedFiles).each(function(i,elem) {
|
||||
// TODO: refactor this with the table row creation code
|
||||
var newtr = $('<tr></tr>')
|
||||
.attr('data-dir', dir)
|
||||
.attr('data-file', elem.name)
|
||||
.attr('data-origin', elem.origin);
|
||||
newtr.append($('<td class="filename"></td>').text(elem.name).css('background-size', 32));
|
||||
newtr.append($('<td class="size"></td>').text(OC.Util.humanFileSize(elem.size, false, false)));
|
||||
tbody.append(newtr);
|
||||
if (elem.type === 'dir') {
|
||||
newtr.find('td.filename')
|
||||
.css('background-image', 'url(' + OC.MimeType.getIconUrl('folder') + ')');
|
||||
} else {
|
||||
var path = dir + '/' + elem.name;
|
||||
Files.lazyLoadPreview(path, elem.mimetype, function(previewpath) {
|
||||
newtr.find('td.filename')
|
||||
.css('background-image', 'url(' + previewpath + ')');
|
||||
}, null, null, elem.etag);
|
||||
}
|
||||
});
|
||||
|
||||
return dragshadow;
|
||||
};
|
||||
|
||||
//options for file drag/drop
|
||||
//start&stop handlers needs some cleaning up
|
||||
// TODO: move to FileList class
|
||||
var dragOptions={
|
||||
revert: 'invalid',
|
||||
revertDuration: 300,
|
||||
opacity: 0.7,
|
||||
cursorAt: { left: 24, top: 18 },
|
||||
helper: createDragShadow,
|
||||
cursor: 'move',
|
||||
|
||||
start: function(event, ui){
|
||||
var $selectedFiles = $('td.filename input:checkbox:checked');
|
||||
if (!$selectedFiles.length) {
|
||||
$selectedFiles = $(this);
|
||||
}
|
||||
$selectedFiles.closest('tr').addClass('animate-opacity dragging');
|
||||
$selectedFiles.closest('tr').filter('.ui-droppable').droppable( 'disable' );
|
||||
// Show breadcrumbs menu
|
||||
$('.crumbmenu').addClass('canDropChildren');
|
||||
|
||||
},
|
||||
stop: function(event, ui) {
|
||||
var $selectedFiles = $('td.filename input:checkbox:checked');
|
||||
if (!$selectedFiles.length) {
|
||||
$selectedFiles = $(this);
|
||||
}
|
||||
|
||||
var $tr = $selectedFiles.closest('tr');
|
||||
$tr.removeClass('dragging');
|
||||
$tr.filter('.ui-droppable').droppable( 'enable' );
|
||||
|
||||
setTimeout(function() {
|
||||
$tr.removeClass('animate-opacity');
|
||||
}, 300);
|
||||
// Hide breadcrumbs menu
|
||||
$('.crumbmenu').removeClass('canDropChildren');
|
||||
},
|
||||
drag: function(event, ui) {
|
||||
// Prevent scrolling when hovering .files-controls
|
||||
if ($(event.originalEvent.target).parents('.files-controls').length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
/** @type {JQuery<HTMLDivElement>} */
|
||||
const scrollingArea = FileList.$container;
|
||||
|
||||
// Get the top and bottom scroll trigger y positions
|
||||
const containerHeight = scrollingArea.innerHeight() ?? 0
|
||||
const scrollTriggerArea = Math.min(Math.floor(containerHeight / 2), 100);
|
||||
const bottomTriggerY = containerHeight - scrollTriggerArea;
|
||||
const topTriggerY = scrollTriggerArea;
|
||||
|
||||
// Get the cursor position relative to the container
|
||||
const containerOffset = scrollingArea.offset() ?? {left: 0, top: 0}
|
||||
const cursorPositionY = event.pageY - containerOffset.top
|
||||
|
||||
const currentScrollTop = scrollingArea.scrollTop() ?? 0
|
||||
|
||||
if (cursorPositionY < topTriggerY) {
|
||||
scrollingArea.scrollTop(currentScrollTop - 10)
|
||||
} else if (cursorPositionY > bottomTriggerY) {
|
||||
scrollingArea.scrollTop(currentScrollTop + 10)
|
||||
}
|
||||
}
|
||||
};
|
||||
// sane browsers support using the distance option
|
||||
if ( $('html.ie').length === 0) {
|
||||
dragOptions['distance'] = 20;
|
||||
}
|
||||
|
||||
// TODO: move to FileList class
|
||||
var folderDropOptions = {
|
||||
hoverClass: "canDrop",
|
||||
drop: function( event, ui ) {
|
||||
// don't allow moving a file into a selected folder
|
||||
/* global FileList */
|
||||
if ($(event.target).parents('tr').find('td input:first').prop('checked') === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var $tr = $(this).closest('tr');
|
||||
if (($tr.data('permissions') & OC.PERMISSION_CREATE) === 0) {
|
||||
FileList._showPermissionDeniedNotification();
|
||||
return false;
|
||||
}
|
||||
var targetPath = FileList.getCurrentDirectory() + '/' + $tr.data('file');
|
||||
|
||||
var files = FileList.getSelectedFiles();
|
||||
if (files.length === 0) {
|
||||
// single one selected without checkbox?
|
||||
files = _.map(ui.helper.find('tr'), function(el) {
|
||||
return FileList.elementToFile($(el));
|
||||
});
|
||||
}
|
||||
|
||||
FileList.move(_.pluck(files, 'name'), targetPath);
|
||||
},
|
||||
tolerance: 'pointer'
|
||||
};
|
||||
|
||||
// for backward compatibility
|
||||
window.Files = OCA.Files.Files;
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function() {
|
||||
/**
|
||||
* The FileSummary class encapsulates the file summary values and
|
||||
* the logic to render it in the given container
|
||||
*
|
||||
* @constructs FileSummary
|
||||
* @memberof OCA.Files
|
||||
*
|
||||
* @param $tr table row element
|
||||
* @param {OC.Backbone.Model} [options.filesConfig] files app configuration
|
||||
*/
|
||||
var FileSummary = function($tr, options) {
|
||||
options = options || {};
|
||||
var self = this;
|
||||
this.$el = $tr;
|
||||
var filesConfig = options.config;
|
||||
if (filesConfig) {
|
||||
this._showHidden = !!filesConfig.show_hidden;
|
||||
window._nc_event_bus.subscribe('files:config:updated', ({ key, value }) => {
|
||||
if (key === 'show_hidden') {
|
||||
self._showHidden = !!value;
|
||||
self.update();
|
||||
}
|
||||
});
|
||||
}
|
||||
this.clear();
|
||||
this.render();
|
||||
};
|
||||
|
||||
FileSummary.prototype = {
|
||||
_showHidden: null,
|
||||
|
||||
summary: {
|
||||
totalFiles: 0,
|
||||
totalDirs: 0,
|
||||
totalHidden: 0,
|
||||
totalSize: 0,
|
||||
filter:'',
|
||||
sumIsPending:false
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether the given file info must be hidden
|
||||
*
|
||||
* @param {OC.Files.FileInfo} fileInfo file info
|
||||
*
|
||||
* @return {boolean} true if the file is a hidden file, false otherwise
|
||||
*/
|
||||
_isHiddenFile: function(file) {
|
||||
return file.name && file.name.charAt(0) === '.';
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds file
|
||||
* @param {OC.Files.FileInfo} file file to add
|
||||
* @param {boolean} update whether to update the display
|
||||
*/
|
||||
add: function(file, update) {
|
||||
if (file.name && file.name.toLowerCase().indexOf(this.summary.filter) === -1) {
|
||||
return;
|
||||
}
|
||||
if (file.type === 'dir' || file.mime === 'httpd/unix-directory') {
|
||||
this.summary.totalDirs++;
|
||||
}
|
||||
else {
|
||||
this.summary.totalFiles++;
|
||||
}
|
||||
if (this._isHiddenFile(file)) {
|
||||
this.summary.totalHidden++;
|
||||
}
|
||||
|
||||
var size = parseInt(file.size, 10) || 0;
|
||||
if (size >=0) {
|
||||
this.summary.totalSize += size;
|
||||
} else {
|
||||
this.summary.sumIsPending = true;
|
||||
}
|
||||
if (!!update) {
|
||||
this.update();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Removes file
|
||||
* @param {OC.Files.FileInfo} file file to remove
|
||||
* @param {boolean} update whether to update the display
|
||||
*/
|
||||
remove: function(file, update) {
|
||||
if (file.name && file.name.toLowerCase().indexOf(this.summary.filter) === -1) {
|
||||
return;
|
||||
}
|
||||
if (file.type === 'dir' || file.mime === 'httpd/unix-directory') {
|
||||
this.summary.totalDirs--;
|
||||
}
|
||||
else {
|
||||
this.summary.totalFiles--;
|
||||
}
|
||||
if (this._isHiddenFile(file)) {
|
||||
this.summary.totalHidden--;
|
||||
}
|
||||
var size = parseInt(file.size, 10) || 0;
|
||||
if (size >=0) {
|
||||
this.summary.totalSize -= size;
|
||||
}
|
||||
if (!!update) {
|
||||
this.update();
|
||||
}
|
||||
},
|
||||
setFilter: function(filter, files){
|
||||
this.summary.filter = filter.toLowerCase();
|
||||
this.calculate(files);
|
||||
},
|
||||
/**
|
||||
* Returns the total of files and directories
|
||||
*/
|
||||
getTotal: function() {
|
||||
return this.summary.totalDirs + this.summary.totalFiles;
|
||||
},
|
||||
/**
|
||||
* Recalculates the summary based on the given files array
|
||||
* @param files array of files
|
||||
*/
|
||||
calculate: function(files) {
|
||||
var file;
|
||||
var summary = {
|
||||
totalDirs: 0,
|
||||
totalFiles: 0,
|
||||
totalHidden: 0,
|
||||
totalSize: 0,
|
||||
filter: this.summary.filter,
|
||||
sumIsPending: false
|
||||
};
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
file = files[i];
|
||||
if (file.name && file.name.toLowerCase().indexOf(this.summary.filter) === -1) {
|
||||
continue;
|
||||
}
|
||||
if (file.type === 'dir' || file.mime === 'httpd/unix-directory') {
|
||||
summary.totalDirs++;
|
||||
}
|
||||
else {
|
||||
summary.totalFiles++;
|
||||
}
|
||||
if (this._isHiddenFile(file)) {
|
||||
summary.totalHidden++;
|
||||
}
|
||||
var size = parseInt(file.size, 10) || 0;
|
||||
if (size >=0) {
|
||||
summary.totalSize += size;
|
||||
} else {
|
||||
summary.sumIsPending = true;
|
||||
}
|
||||
}
|
||||
this.setSummary(summary);
|
||||
},
|
||||
/**
|
||||
* Clears the summary
|
||||
*/
|
||||
clear: function() {
|
||||
this.calculate([]);
|
||||
},
|
||||
/**
|
||||
* Sets the current summary values
|
||||
* @param summary map
|
||||
*/
|
||||
setSummary: function(summary) {
|
||||
this.summary = summary;
|
||||
if (typeof this.summary.filter === 'undefined') {
|
||||
this.summary.filter = '';
|
||||
}
|
||||
this.update();
|
||||
},
|
||||
|
||||
_infoTemplate: function(data) {
|
||||
/* NOTE: To update the template make changes in filesummary.handlebars
|
||||
* and run:
|
||||
*
|
||||
* handlebars -n OCA.Files.FileSummary.Templates filesummary.handlebars -f filesummary_template.js
|
||||
*/
|
||||
return OCA.Files.Templates['filesummary'](_.extend({
|
||||
connectorLabel: t('files', '{dirs} and {files}', {dirs: '', files: ''})
|
||||
}, data));
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the file summary element
|
||||
*/
|
||||
update: function() {
|
||||
if (!this.$el) {
|
||||
return;
|
||||
}
|
||||
if (!this.summary.totalFiles && !this.summary.totalDirs) {
|
||||
this.$el.addClass('hidden');
|
||||
return;
|
||||
}
|
||||
// There's a summary and data -> Update the summary
|
||||
this.$el.removeClass('hidden');
|
||||
var $dirInfo = this.$el.find('.dirinfo');
|
||||
var $fileInfo = this.$el.find('.fileinfo');
|
||||
var $connector = this.$el.find('.connector');
|
||||
var $filterInfo = this.$el.find('.filter');
|
||||
var $hiddenInfo = this.$el.find('.hiddeninfo');
|
||||
|
||||
// Substitute old content with new translations
|
||||
$dirInfo.html(n('files', '%n folder', '%n folders', this.summary.totalDirs));
|
||||
$fileInfo.html(n('files', '%n file', '%n files', this.summary.totalFiles));
|
||||
$hiddenInfo.html(' (' + n('files', 'including %n hidden', 'including %n hidden', this.summary.totalHidden) + ')');
|
||||
var fileSize = this.summary.sumIsPending ? t('files', 'Pending') : OC.Util.humanFileSize(this.summary.totalSize, false, false);
|
||||
this.$el.find('.filesize').html(fileSize);
|
||||
|
||||
// Show only what's necessary (may be hidden)
|
||||
if (this.summary.totalDirs === 0) {
|
||||
$dirInfo.addClass('hidden');
|
||||
$connector.addClass('hidden');
|
||||
} else {
|
||||
$dirInfo.removeClass('hidden');
|
||||
}
|
||||
if (this.summary.totalFiles === 0) {
|
||||
$fileInfo.addClass('hidden');
|
||||
$connector.addClass('hidden');
|
||||
} else {
|
||||
$fileInfo.removeClass('hidden');
|
||||
}
|
||||
if (this.summary.totalDirs > 0 && this.summary.totalFiles > 0) {
|
||||
$connector.removeClass('hidden');
|
||||
}
|
||||
$hiddenInfo.toggleClass('hidden', this.summary.totalHidden === 0 || this._showHidden)
|
||||
if (this.summary.filter === '') {
|
||||
$filterInfo.html('');
|
||||
$filterInfo.addClass('hidden');
|
||||
} else {
|
||||
$filterInfo.html(' ' + n('files', 'matches "{filter}"', 'match "{filter}"', this.summary.totalDirs + this.summary.totalFiles, {filter: this.summary.filter}));
|
||||
$filterInfo.removeClass('hidden');
|
||||
}
|
||||
},
|
||||
render: function() {
|
||||
if (!this.$el) {
|
||||
return;
|
||||
}
|
||||
var summary = this.summary;
|
||||
|
||||
// don't show the filesize column, if filesize is NaN (e.g. in trashbin)
|
||||
var fileSize = '';
|
||||
if (!isNaN(summary.totalSize)) {
|
||||
fileSize = summary.sumIsPending ? t('files', 'Pending') : OC.Util.humanFileSize(summary.totalSize, false, false);
|
||||
fileSize = '<td class="filesize">' + fileSize + '</td>';
|
||||
}
|
||||
|
||||
var $summary = $(
|
||||
'<td class="filesummary">'+ this._infoTemplate() + '</td>' +
|
||||
fileSize +
|
||||
'<td class="date"></td>'
|
||||
);
|
||||
this.$el.addClass('hidden');
|
||||
this.$el.append($summary);
|
||||
this.update();
|
||||
}
|
||||
};
|
||||
OCA.Files.FileSummary = FileSummary;
|
||||
})();
|
||||
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function (OCA) {
|
||||
|
||||
OCA.Files = OCA.Files || {};
|
||||
|
||||
/**
|
||||
* @namespace OCA.Files.GotoPlugin
|
||||
*
|
||||
*/
|
||||
OCA.Files.GotoPlugin = {
|
||||
name: 'Goto',
|
||||
|
||||
disallowedLists: [
|
||||
'files',
|
||||
'trashbin'
|
||||
],
|
||||
|
||||
attach: function (fileList) {
|
||||
if (this.disallowedLists.indexOf(fileList.id) !== -1) {
|
||||
return;
|
||||
}
|
||||
// lists where the "Open" default action is disabled should
|
||||
// also have the goto action disabled
|
||||
if (fileList._defaultFileActionsDisabled) {
|
||||
return
|
||||
}
|
||||
var fileActions = fileList.fileActions;
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'Goto',
|
||||
displayName: t('files', 'View in folder'),
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_ALL,
|
||||
iconClass: 'icon-goto nav-icon-extstoragemounts',
|
||||
type: OCA.Files.FileActions.TYPE_DROPDOWN,
|
||||
actionHandler: function (fileName, context) {
|
||||
var fileModel = context.fileInfoModel;
|
||||
OCA.Files.Sidebar.close();
|
||||
OCA.Files.App.setActiveView('files', { silent: true });
|
||||
OCA.Files.App.fileList.changeDirectory(fileModel.get('path'), true, true).then(function() {
|
||||
OCA.Files.App.fileList.scrollTo(fileModel.get('name'));
|
||||
});
|
||||
},
|
||||
render: function (actionSpec, isDefault, context) {
|
||||
return fileActions._defaultRenderAction.call(fileActions, actionSpec, isDefault, context)
|
||||
.removeClass('permanent');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
})(OCA);
|
||||
|
||||
OC.Plugins.register('OCA.Files.FileList', OCA.Files.GotoPlugin);
|
||||
|
||||
91
apps/files/js/jquery-visibility.js
vendored
91
apps/files/js/jquery-visibility.js
vendored
|
|
@ -1,91 +0,0 @@
|
|||
/*!
|
||||
* jquery-visibility v1.0.11
|
||||
* Page visibility shim for jQuery.
|
||||
*
|
||||
* Project Website: http://mths.be/visibility
|
||||
*
|
||||
* @version 1.0.11
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-FileCopyrightText: Mathias Bynens
|
||||
* SPDX-FileCopyrightText: Jan Paepke
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
;(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['jquery'], function ($) {
|
||||
return factory(root, $);
|
||||
});
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node/CommonJS
|
||||
module.exports = factory(root, require('jquery'));
|
||||
} else {
|
||||
// Browser globals
|
||||
factory(root, jQuery);
|
||||
}
|
||||
}(this, function(window, $, undefined) {
|
||||
"use strict";
|
||||
|
||||
var
|
||||
document = window.document,
|
||||
property, // property name of document, that stores page visibility
|
||||
vendorPrefixes = ['webkit', 'o', 'ms', 'moz', ''],
|
||||
$support = $.support || {},
|
||||
// In Opera, `'onfocusin' in document == true`, hence the extra `hasFocus` check to detect IE-like behavior
|
||||
eventName = 'onfocusin' in document && 'hasFocus' in document ?
|
||||
'focusin focusout' :
|
||||
'focus blur';
|
||||
|
||||
var prefix;
|
||||
while ((prefix = vendorPrefixes.pop()) !== undefined) {
|
||||
property = (prefix ? prefix + 'H': 'h') + 'idden';
|
||||
$support.pageVisibility = document[property] !== undefined;
|
||||
if ($support.pageVisibility) {
|
||||
eventName = prefix + 'visibilitychange';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// normalize to and update document hidden property
|
||||
function updateState() {
|
||||
if (property !== 'hidden') {
|
||||
document.hidden = $support.pageVisibility ? document[property] : undefined;
|
||||
}
|
||||
}
|
||||
updateState();
|
||||
|
||||
$(/blur$/.test(eventName) ? window : document).on(eventName, function(event) {
|
||||
var type = event.type;
|
||||
var originalEvent = event.originalEvent;
|
||||
|
||||
// Avoid errors from triggered native events for which `originalEvent` is
|
||||
// not available.
|
||||
if (!originalEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
var toElement = originalEvent.toElement;
|
||||
|
||||
// If it’s a `{focusin,focusout}` event (IE), `fromElement` and `toElement`
|
||||
// should both be `null` or `undefined`; else, the page visibility hasn’t
|
||||
// changed, but the user just clicked somewhere in the doc. In IE9, we need
|
||||
// to check the `relatedTarget` property instead.
|
||||
if (
|
||||
!/^focus./.test(type) || (
|
||||
toElement === undefined &&
|
||||
originalEvent.fromElement === undefined &&
|
||||
originalEvent.relatedTarget === undefined
|
||||
)
|
||||
) {
|
||||
$(document).triggerHandler(
|
||||
property && document[property] || /^(?:blur|focusout)$/.test(type) ?
|
||||
'hide' :
|
||||
'show'
|
||||
);
|
||||
}
|
||||
// and update the current state
|
||||
updateState();
|
||||
});
|
||||
}));
|
||||
1504
apps/files/js/jquery.fileupload.js
vendored
1504
apps/files/js/jquery.fileupload.js
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -1,170 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2012-2014 ownCloud, Inc.
|
||||
* SPDX-FileCopyrightText: 2012 Erik Sargent <esthepiking at gmail dot com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
/*****************************
|
||||
* Keyboard shortcuts for Files app
|
||||
* ctrl/cmd+n: new folder
|
||||
* ctrl/cmd+shift+n: new file
|
||||
* esc (while new file context menu is open): close menu
|
||||
* up/down: select file/folder
|
||||
* enter: open file/folder
|
||||
* delete/backspace: delete file/folder
|
||||
*****************************/
|
||||
(function(Files) {
|
||||
var keys = [];
|
||||
var keyCodes = {
|
||||
shift: 16,
|
||||
n: 78,
|
||||
cmdFirefox: 224,
|
||||
cmdOpera: 17,
|
||||
leftCmdWebKit: 91,
|
||||
rightCmdWebKit: 93,
|
||||
ctrl: 17,
|
||||
esc: 27,
|
||||
downArrow: 40,
|
||||
upArrow: 38,
|
||||
enter: 13,
|
||||
del: 46
|
||||
};
|
||||
|
||||
function removeA(arr) {
|
||||
var what, a = arguments,
|
||||
L = a.length,
|
||||
ax;
|
||||
while (L > 1 && arr.length) {
|
||||
what = a[--L];
|
||||
while ((ax = arr.indexOf(what)) !== -1) {
|
||||
arr.splice(ax, 1);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function newFile() {
|
||||
$("#new").addClass("active");
|
||||
$(".popup.popupTop").toggle(true);
|
||||
$('#new li[data-type="file"]').trigger('click');
|
||||
removeA(keys, keyCodes.n);
|
||||
}
|
||||
|
||||
function newFolder() {
|
||||
$("#new").addClass("active");
|
||||
$(".popup.popupTop").toggle(true);
|
||||
$('#new li[data-type="folder"]').trigger('click');
|
||||
removeA(keys, keyCodes.n);
|
||||
}
|
||||
|
||||
function esc() {
|
||||
$(".files-controls").trigger('click');
|
||||
}
|
||||
|
||||
function down() {
|
||||
var select = -1;
|
||||
$(".files-fileList tr").each(function(index) {
|
||||
if ($(this).hasClass("mouseOver")) {
|
||||
select = index + 1;
|
||||
$(this).removeClass("mouseOver");
|
||||
}
|
||||
});
|
||||
if (select === -1) {
|
||||
$(".files-fileList tr:first").addClass("mouseOver");
|
||||
} else {
|
||||
$(".files-fileList tr").each(function(index) {
|
||||
if (index === select) {
|
||||
$(this).addClass("mouseOver");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function up() {
|
||||
var select = -1;
|
||||
$(".files-fileList tr").each(function(index) {
|
||||
if ($(this).hasClass("mouseOver")) {
|
||||
select = index - 1;
|
||||
$(this).removeClass("mouseOver");
|
||||
}
|
||||
});
|
||||
if (select === -1) {
|
||||
$(".files-fileList tr:last").addClass("mouseOver");
|
||||
} else {
|
||||
$(".files-fileList tr").each(function(index) {
|
||||
if (index === select) {
|
||||
$(this).addClass("mouseOver");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function enter() {
|
||||
$(".files-fileList tr").each(function(index) {
|
||||
if ($(this).hasClass("mouseOver")) {
|
||||
$(this).removeClass("mouseOver");
|
||||
$(this).find("span.nametext").trigger('click');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function del() {
|
||||
$(".files-fileList tr").each(function(index) {
|
||||
if ($(this).hasClass("mouseOver")) {
|
||||
$(this).removeClass("mouseOver");
|
||||
$(this).find("a.action.delete").trigger('click');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function rename() {
|
||||
$(".files-fileList tr").each(function(index) {
|
||||
if ($(this).hasClass("mouseOver")) {
|
||||
$(this).removeClass("mouseOver");
|
||||
$(this).find("a[data-action='Rename']").trigger('click');
|
||||
}
|
||||
});
|
||||
}
|
||||
Files.bindKeyboardShortcuts = function(document, $) {
|
||||
$(document).keydown(function(event) { //check for modifier keys
|
||||
if(!$(event.target).is('body')) {
|
||||
return;
|
||||
}
|
||||
var preventDefault = false;
|
||||
if ($.inArray(event.keyCode, keys) === -1) {
|
||||
keys.push(event.keyCode);
|
||||
}
|
||||
if (
|
||||
$.inArray(keyCodes.n, keys) !== -1 && ($.inArray(keyCodes.cmdFirefox, keys) !== -1 || $.inArray(keyCodes.cmdOpera, keys) !== -1 || $.inArray(keyCodes.leftCmdWebKit, keys) !== -1 || $.inArray(keyCodes.rightCmdWebKit, keys) !== -1 || $.inArray(keyCodes.ctrl, keys) !== -1 || event.ctrlKey)) {
|
||||
preventDefault = true; //new file/folder prevent browser from responding
|
||||
}
|
||||
if (preventDefault) {
|
||||
event.preventDefault(); //Prevent web browser from responding
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
$(document).keyup(function(event) {
|
||||
// do your event.keyCode checks in here
|
||||
if (
|
||||
$.inArray(keyCodes.n, keys) !== -1 && ($.inArray(keyCodes.cmdFirefox, keys) !== -1 || $.inArray(keyCodes.cmdOpera, keys) !== -1 || $.inArray(keyCodes.leftCmdWebKit, keys) !== -1 || $.inArray(keyCodes.rightCmdWebKit, keys) !== -1 || $.inArray(keyCodes.ctrl, keys) !== -1 || event.ctrlKey)) {
|
||||
if ($.inArray(keyCodes.shift, keys) !== -1) { //16=shift, New File
|
||||
newFile();
|
||||
} else { //New Folder
|
||||
newFolder();
|
||||
}
|
||||
} else if ($("#new").hasClass("active") && $.inArray(keyCodes.esc, keys) !== -1) { //close new window
|
||||
esc();
|
||||
} else if ($.inArray(keyCodes.downArrow, keys) !== -1) { //select file
|
||||
down();
|
||||
} else if ($.inArray(keyCodes.upArrow, keys) !== -1) { //select file
|
||||
up();
|
||||
} else if (!$("#new").hasClass("active") && $.inArray(keyCodes.enter, keys) !== -1) { //open file
|
||||
enter();
|
||||
} else if (!$("#new").hasClass("active") && $.inArray(keyCodes.del, keys) !== -1) { //delete file
|
||||
del();
|
||||
}
|
||||
removeA(keys, event.keyCode);
|
||||
});
|
||||
};
|
||||
})((OCA.Files && OCA.Files.Files) || {});
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function() {
|
||||
/**
|
||||
* @class OCA.Files.MainFileInfoDetailView
|
||||
* @classdesc
|
||||
*
|
||||
* Displays main details about a file
|
||||
*
|
||||
*/
|
||||
var MainFileInfoDetailView = OCA.Files.DetailFileInfoView.extend(
|
||||
/** @lends OCA.Files.MainFileInfoDetailView.prototype */ {
|
||||
|
||||
className: 'mainFileInfoView',
|
||||
|
||||
/**
|
||||
* Associated file list instance, for file actions
|
||||
*
|
||||
* @type {OCA.Files.FileList}
|
||||
*/
|
||||
_fileList: null,
|
||||
|
||||
/**
|
||||
* File actions
|
||||
*
|
||||
* @type {OCA.Files.FileActions}
|
||||
*/
|
||||
_fileActions: null,
|
||||
|
||||
/**
|
||||
* @type {OCA.Files.SidebarPreviewManager}
|
||||
*/
|
||||
_previewManager: null,
|
||||
|
||||
events: {
|
||||
'click a.action-favorite': '_onClickFavorite',
|
||||
'click a.action-default': '_onClickDefaultAction',
|
||||
'click a.permalink': '_onClickPermalink',
|
||||
'focus .permalink-field>input': '_onFocusPermalink'
|
||||
},
|
||||
|
||||
template: function(data) {
|
||||
return OCA.Files.Templates['mainfileinfodetailsview'](data);
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
options = options || {};
|
||||
this._fileList = options.fileList;
|
||||
this._fileActions = options.fileActions;
|
||||
if (!this._fileList) {
|
||||
throw 'Missing required parameter "fileList"';
|
||||
}
|
||||
if (!this._fileActions) {
|
||||
throw 'Missing required parameter "fileActions"';
|
||||
}
|
||||
this._previewManager = new OCA.Files.SidebarPreviewManager(this._fileList);
|
||||
|
||||
this._setupClipboard();
|
||||
},
|
||||
|
||||
_setupClipboard: function() {
|
||||
var clipboard = new Clipboard('.permalink');
|
||||
clipboard.on('success', function(e) {
|
||||
OC.Notification.show(t('files', 'Direct link was copied (only works for people who have access to this file/folder)'), {type: 'success'});
|
||||
});
|
||||
clipboard.on('error', function(e) {
|
||||
var $row = this.$('.permalink-field');
|
||||
$row.toggleClass('hidden');
|
||||
if (!$row.hasClass('hidden')) {
|
||||
$row.find('>input').focus();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_onClickPermalink: function(e) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
},
|
||||
|
||||
_onFocusPermalink: function() {
|
||||
this.$('.permalink-field>input').select();
|
||||
},
|
||||
|
||||
_onClickFavorite: function(event) {
|
||||
event.preventDefault();
|
||||
this._fileActions.triggerAction('Favorite', this.model, this._fileList);
|
||||
},
|
||||
|
||||
_onClickDefaultAction: function(event) {
|
||||
event.preventDefault();
|
||||
this._fileActions.triggerAction(null, this.model, this._fileList);
|
||||
},
|
||||
|
||||
_onModelChanged: function() {
|
||||
// simply re-render
|
||||
this.render();
|
||||
},
|
||||
|
||||
_makePermalink: function(fileId) {
|
||||
var baseUrl = OC.getProtocol() + '://' + OC.getHost();
|
||||
return baseUrl + OC.generateUrl('/f/{fileId}', {fileId: fileId});
|
||||
},
|
||||
|
||||
setFileInfo: function(fileInfo) {
|
||||
if (this.model) {
|
||||
this.model.off('change', this._onModelChanged, this);
|
||||
}
|
||||
this.model = fileInfo;
|
||||
if (this.model) {
|
||||
this.model.on('change', this._onModelChanged, this);
|
||||
}
|
||||
|
||||
if (this.model) {
|
||||
var properties = [];
|
||||
if( !this.model.has('size') ) {
|
||||
properties.push(OC.Files.Client.PROPERTY_SIZE);
|
||||
properties.push(OC.Files.Client.PROPERTY_GETCONTENTLENGTH);
|
||||
}
|
||||
|
||||
if( properties.length > 0){
|
||||
this.model.reloadProperties(properties);
|
||||
}
|
||||
}
|
||||
|
||||
this.render();
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders this details view
|
||||
*/
|
||||
render: function() {
|
||||
this.trigger('pre-render');
|
||||
|
||||
if (this.model) {
|
||||
var isFavorite = (this.model.get('tags') || []).indexOf(OC.TAG_FAVORITE) >= 0;
|
||||
var availableActions = this._fileActions.get(
|
||||
this.model.get('mimetype'),
|
||||
this.model.get('type'),
|
||||
this.model.get('permissions'),
|
||||
this.model.get('name')
|
||||
);
|
||||
var hasFavoriteAction = 'Favorite' in availableActions;
|
||||
this.$el.html(this.template({
|
||||
type: this.model.isImage()? 'image': '',
|
||||
nameLabel: t('files', 'Name'),
|
||||
name: this.model.get('displayName') || this.model.get('name'),
|
||||
pathLabel: t('files', 'Path'),
|
||||
path: this.model.get('path'),
|
||||
hasSize: this.model.has('size'),
|
||||
sizeLabel: t('files', 'Size'),
|
||||
size: OC.Util.humanFileSize(this.model.get('size'), true, false),
|
||||
altSize: n('files', '%n byte', '%n bytes', this.model.get('size')),
|
||||
dateLabel: t('files', 'Modified'),
|
||||
altDate: OC.Util.formatDate(this.model.get('mtime')),
|
||||
timestamp: this.model.get('mtime'),
|
||||
date: OC.Util.relativeModifiedDate(this.model.get('mtime')),
|
||||
hasFavoriteAction: hasFavoriteAction,
|
||||
starAltText: isFavorite ? t('files', 'Favorited') : t('files', 'Favorite'),
|
||||
starClass: isFavorite ? 'icon-starred' : 'icon-star',
|
||||
permalink: this._makePermalink(this.model.get('id')),
|
||||
permalinkTitle: t('files', 'Copy direct link (only works for people who have access to this file/folder)')
|
||||
}));
|
||||
|
||||
// TODO: we really need OC.Previews
|
||||
var $iconDiv = this.$el.find('.thumbnail');
|
||||
var $container = this.$el.find('.thumbnailContainer');
|
||||
if (!this.model.isDirectory()) {
|
||||
$iconDiv.addClass('icon-loading icon-32');
|
||||
this._previewManager.loadPreview(this.model, $iconDiv, $container);
|
||||
} else {
|
||||
var iconUrl = this.model.get('icon') || OC.MimeType.getIconUrl('dir');
|
||||
if (typeof this.model.get('mountType') !== 'undefined') {
|
||||
iconUrl = OC.MimeType.getIconUrl('dir-' + this.model.get('mountType'))
|
||||
}
|
||||
$iconDiv.css('background-image', 'url("' + iconUrl + '")');
|
||||
}
|
||||
} else {
|
||||
this.$el.empty();
|
||||
}
|
||||
this.delegateEvents();
|
||||
|
||||
this.trigger('post-render');
|
||||
}
|
||||
});
|
||||
|
||||
OCA.Files.MainFileInfoDetailView = MainFileInfoDetailView;
|
||||
})();
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
[
|
||||
"app.js",
|
||||
"breadcrumb.js",
|
||||
"detailfileinfoview.js",
|
||||
"detailsview.js",
|
||||
"detailtabview.js",
|
||||
"file-upload.js",
|
||||
"fileactions.js",
|
||||
"fileactionsmenu.js",
|
||||
"fileinfomodel.js",
|
||||
"filelist.js",
|
||||
"filemultiselectmenu.js",
|
||||
"files.js",
|
||||
"filesummary.js",
|
||||
"gotoplugin.js",
|
||||
"jquery-visibility.js",
|
||||
"jquery.fileupload.js",
|
||||
"keyboardshortcuts.js",
|
||||
"mainfileinfodetailview.js",
|
||||
"newfilemenu.js",
|
||||
"operationprogressbar.js",
|
||||
"recentfilelist.js",
|
||||
"semaphore.js",
|
||||
"sidebarpreviewmanager.js",
|
||||
"sidebarpreviewtext.js",
|
||||
"tagsplugin.js",
|
||||
"templates.js"
|
||||
]
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
|
@ -1,261 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/* global Files */
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* Construct a new NewFileMenu instance
|
||||
* @constructs NewFileMenu
|
||||
*
|
||||
* @memberof OCA.Files
|
||||
*/
|
||||
var NewFileMenu = OC.Backbone.View.extend({
|
||||
tagName: 'div',
|
||||
// Menu is opened by default because it's rendered on "add-button" click
|
||||
className: 'newFileMenu popovermenu bubble menu open menu-left',
|
||||
|
||||
events: {
|
||||
'click .menuitem': '_onClickAction'
|
||||
},
|
||||
|
||||
/**
|
||||
* @type OCA.Files.FileList
|
||||
*/
|
||||
fileList: null,
|
||||
|
||||
initialize: function(options) {
|
||||
var self = this;
|
||||
var $uploadEl = $('#file_upload_start');
|
||||
if ($uploadEl.length) {
|
||||
$uploadEl.on('fileuploadstart', function() {
|
||||
self.trigger('actionPerformed', 'upload');
|
||||
});
|
||||
} else {
|
||||
console.warn('Missing upload element "file_upload_start"');
|
||||
}
|
||||
|
||||
this.fileList = options && options.fileList;
|
||||
|
||||
this._menuItems = [{
|
||||
id: 'folder',
|
||||
displayName: t('files', 'New folder'),
|
||||
templateName: t('files', 'New folder'),
|
||||
iconClass: 'icon-folder',
|
||||
fileType: 'folder',
|
||||
actionLabel: t('files', 'Create new folder'),
|
||||
actionHandler: function(name) {
|
||||
self.fileList.createDirectory(name);
|
||||
}
|
||||
}];
|
||||
|
||||
OC.Plugins.attach('OCA.Files.NewFileMenu', this);
|
||||
},
|
||||
|
||||
template: function(data) {
|
||||
return OCA.Files.Templates['newfilemenu'](data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler whenever an action has been clicked within the menu
|
||||
*
|
||||
* @param {Object} event event object
|
||||
*/
|
||||
_onClickAction: function(event) {
|
||||
var $target = $(event.target);
|
||||
if (!$target.hasClass('menuitem')) {
|
||||
$target = $target.closest('.menuitem');
|
||||
}
|
||||
var action = $target.attr('data-action');
|
||||
// note: clicking the upload label will automatically
|
||||
// set the focus on the "file_upload_start" hidden field
|
||||
// which itself triggers the upload dialog.
|
||||
// Currently the upload logic is still in file-upload.js and filelist.js
|
||||
if (action === 'upload') {
|
||||
OC.hideMenus();
|
||||
} else {
|
||||
var actionItem = _.filter(this._menuItems, function(item) {
|
||||
return item.id === action
|
||||
}).pop();
|
||||
if (typeof actionItem.useInput === 'undefined' || actionItem.useInput === true) {
|
||||
event.preventDefault();
|
||||
this.$el.find('.menuitem.active').removeClass('active');
|
||||
$target.addClass('active');
|
||||
this._promptFileName($target);
|
||||
} else {
|
||||
actionItem.actionHandler();
|
||||
OC.hideMenus();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_promptFileName: function($target) {
|
||||
var self = this;
|
||||
|
||||
if ($target.find('form').length) {
|
||||
$target.find('input[type=\'text\']').focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// discard other forms
|
||||
this.$el.find('form').remove();
|
||||
this.$el.find('.displayname').removeClass('hidden');
|
||||
|
||||
$target.find('.displayname').addClass('hidden');
|
||||
|
||||
var newName = $target.attr('data-templatename');
|
||||
var fileType = $target.attr('data-filetype');
|
||||
var actionLabel = $target.attr('data-action-label');
|
||||
var $form = $(OCA.Files.Templates['newfilemenu_filename_form']({
|
||||
fileName: newName,
|
||||
cid: this.cid,
|
||||
fileType: fileType,
|
||||
actionLabel,
|
||||
}));
|
||||
|
||||
//this.trigger('actionPerformed', action);
|
||||
$target.append($form);
|
||||
|
||||
// here comes the OLD code
|
||||
var $input = $form.find('input[type=\'text\']');
|
||||
var $submit = $form.find('input[type=\'submit\']');
|
||||
|
||||
var lastPos;
|
||||
var checkInput = function () {
|
||||
// Special handling for the setup template directory
|
||||
if ($target.attr('data-action') === 'template-init') {
|
||||
return true;
|
||||
}
|
||||
|
||||
var filename = $input.val();
|
||||
try {
|
||||
if (!Files.isFileNameValid(filename)) {
|
||||
// Files.isFileNameValid(filename) throws an exception itself
|
||||
} else if (self.fileList.inList(filename)) {
|
||||
throw t('files', '{newName} already exists', {newName: filename}, undefined, {
|
||||
escape: false
|
||||
});
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
$input.attr('title', error);
|
||||
$input.addClass('error');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// verify filename on typing
|
||||
$input.keyup(function() {
|
||||
if (checkInput()) {
|
||||
$input.removeClass('error');
|
||||
}
|
||||
});
|
||||
|
||||
$submit.click(function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
$form.submit();
|
||||
});
|
||||
|
||||
$input.focus();
|
||||
// pre select name up to the extension
|
||||
lastPos = newName.lastIndexOf('.');
|
||||
if (lastPos === -1) {
|
||||
lastPos = newName.length;
|
||||
}
|
||||
$input.selectRange(0, lastPos);
|
||||
|
||||
$form.submit(function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if (checkInput()) {
|
||||
var newname = $input.val().trim();
|
||||
|
||||
/* Find the right actionHandler that should be called.
|
||||
* Actions is retrieved by using `actionSpec.id` */
|
||||
var action = _.filter(self._menuItems, function(item) {
|
||||
return item.id == $target.attr('data-action');
|
||||
}).pop();
|
||||
action.actionHandler(newname);
|
||||
|
||||
$form.remove();
|
||||
$target.find('.displayname').removeClass('hidden');
|
||||
OC.hideMenus();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new item menu entry in the “New” file menu (in
|
||||
* last position). By clicking on the item, the
|
||||
* `actionHandler` function is called.
|
||||
*
|
||||
* @param {Object} actionSpec item’s properties
|
||||
*/
|
||||
addMenuEntry: function(actionSpec) {
|
||||
this._menuItems.push({
|
||||
id: actionSpec.id,
|
||||
displayName: actionSpec.displayName,
|
||||
templateName: actionSpec.templateName,
|
||||
iconClass: actionSpec.iconClass,
|
||||
fileType: actionSpec.fileType,
|
||||
useInput: actionSpec.useInput,
|
||||
actionLabel: actionSpec.actionLabel,
|
||||
actionHandler: actionSpec.actionHandler,
|
||||
checkFilename: actionSpec.checkFilename,
|
||||
shouldShow: actionSpec.shouldShow,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a menu item from the "New" file menu
|
||||
* @param {string} actionId
|
||||
*/
|
||||
removeMenuEntry: function(actionId) {
|
||||
var index = this._menuItems.findIndex(function (actionSpec) {
|
||||
return actionSpec.id === actionId;
|
||||
});
|
||||
if (index > -1) {
|
||||
this._menuItems.splice(index, 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the menu with the currently set items
|
||||
*/
|
||||
render: function() {
|
||||
const menuItems = this._menuItems.filter(item => !item.shouldShow || (item.shouldShow instanceof Function && item.shouldShow() === true))
|
||||
this.$el.html(this.template({
|
||||
uploadMaxHumanFileSize: 'TODO',
|
||||
uploadLabel: t('files', 'Upload file'),
|
||||
items: menuItems
|
||||
}));
|
||||
|
||||
// Trigger upload action also with keyboard navigation on enter
|
||||
this.$el.find('[for="file_upload_start"]').on('keyup', function(event) {
|
||||
if (event.key === " " || event.key === "Enter") {
|
||||
$('#file_upload_start').trigger('click');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays the menu under the given element
|
||||
*
|
||||
* @param {Object} $target target element
|
||||
*/
|
||||
showAt: function($target) {
|
||||
this.render();
|
||||
OC.showMenu($target, this.$el);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.Files.NewFileMenu = NewFileMenu;
|
||||
|
||||
})();
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var OperationProgressBar = OC.Backbone.View.extend({
|
||||
tagName: 'div',
|
||||
id: 'uploadprogresswrapper',
|
||||
events: {
|
||||
'click button.stop': '_onClickCancel'
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.$el.html(OCA.Files.Templates['operationprogressbar']({
|
||||
textCancelButton: t('Cancel operation')
|
||||
}));
|
||||
this.setProgressBarText(t('Uploading …'), t('…'));
|
||||
},
|
||||
|
||||
hideProgressBar: function() {
|
||||
var self = this;
|
||||
$('#uploadprogresswrapper .stop').fadeOut();
|
||||
$('#uploadprogressbar').fadeOut(function() {
|
||||
self.$el.trigger(new $.Event('resized'));
|
||||
});
|
||||
},
|
||||
|
||||
hideCancelButton: function() {
|
||||
var self = this;
|
||||
$('#uploadprogresswrapper .stop').fadeOut(function() {
|
||||
self.$el.trigger(new $.Event('resized'));
|
||||
});
|
||||
},
|
||||
|
||||
showProgressBar: function(showCancelButton) {
|
||||
if (showCancelButton) {
|
||||
showCancelButton = true;
|
||||
}
|
||||
$('#uploadprogressbar').progressbar({value: 0});
|
||||
if(showCancelButton) {
|
||||
$('#uploadprogresswrapper .stop').show();
|
||||
} else {
|
||||
$('#uploadprogresswrapper .stop').hide();
|
||||
}
|
||||
$('#uploadprogresswrapper .label').show();
|
||||
$('#uploadprogressbar').fadeIn();
|
||||
this.$el.trigger(new $.Event('resized'));
|
||||
},
|
||||
|
||||
setProgressBarValue: function(value) {
|
||||
$('#uploadprogressbar').progressbar({value: value});
|
||||
},
|
||||
|
||||
setProgressBarText: function(textDesktop, textMobile, title) {
|
||||
var labelHtml = OCA.Files.Templates['operationprogressbarlabel']({textDesktop: textDesktop, textMobile: textMobile});
|
||||
$('#uploadprogressbar .ui-progressbar-value').html(labelHtml);
|
||||
$('#uploadprogressbar .ui-progressbar-value>em').addClass('inner');
|
||||
$('#uploadprogressbar>em').replaceWith(labelHtml);
|
||||
$('#uploadprogressbar>em').addClass('outer');
|
||||
if (title) {
|
||||
$('#uploadprogressbar').attr('title', title);
|
||||
$('#uploadprogresswrapper .tooltip-inner').text(title);
|
||||
}
|
||||
if(textDesktop || textMobile) {
|
||||
$('#uploadprogresswrapper .stop').show();
|
||||
}
|
||||
},
|
||||
|
||||
_onClickCancel: function (event) {
|
||||
this.trigger('cancel');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.Files.OperationProgressBar = OperationProgressBar;
|
||||
})(OC, OCA);
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
// HACK: this piece needs to be loaded AFTER the files app (for unit tests)
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
(function (OCA) {
|
||||
/**
|
||||
* @class OCA.Files.RecentFileList
|
||||
* @augments OCA.Files.RecentFileList
|
||||
*
|
||||
* @classdesc Recent file list.
|
||||
* Displays the list of recently modified files
|
||||
*
|
||||
* @param $el container element with existing markup for the .files-controls
|
||||
* and a table
|
||||
* @param [options] map of options, see other parameters
|
||||
*/
|
||||
var RecentFileList = function ($el, options) {
|
||||
options.sorting = {
|
||||
mode: 'mtime',
|
||||
direction: 'desc'
|
||||
};
|
||||
this.initialize($el, options);
|
||||
this._allowSorting = false;
|
||||
};
|
||||
RecentFileList.prototype = _.extend({}, OCA.Files.FileList.prototype,
|
||||
/** @lends OCA.Files.RecentFileList.prototype */ {
|
||||
id: 'recent',
|
||||
appName: t('files', 'Recent'),
|
||||
|
||||
_clientSideSort: true,
|
||||
_allowSelection: false,
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
initialize: function () {
|
||||
OCA.Files.FileList.prototype.initialize.apply(this, arguments);
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
OC.Plugins.attach('OCA.Files.RecentFileList', this);
|
||||
},
|
||||
|
||||
updateEmptyContent: function () {
|
||||
var dir = this.getCurrentDirectory();
|
||||
if (dir === '/') {
|
||||
// root has special permissions
|
||||
this.$el.find('.emptyfilelist.emptycontent').toggleClass('hidden', !this.isEmpty);
|
||||
this.$el.find('.files-filestable thead th').toggleClass('hidden', this.isEmpty);
|
||||
}
|
||||
else {
|
||||
OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
|
||||
getDirectoryPermissions: function () {
|
||||
return OC.PERMISSION_READ | OC.PERMISSION_DELETE;
|
||||
},
|
||||
|
||||
updateStorageStatistics: function () {
|
||||
// no op because it doesn't have
|
||||
// storage info like free space / used space
|
||||
},
|
||||
|
||||
reload: function () {
|
||||
this.showMask();
|
||||
if (this._reloadCall?.abort) {
|
||||
this._reloadCall.abort();
|
||||
}
|
||||
|
||||
// there is only root
|
||||
this._setCurrentDir('/', false);
|
||||
|
||||
this._reloadCall = $.ajax({
|
||||
url: OC.generateUrl('/apps/files/api/v1/recent'),
|
||||
type: 'GET',
|
||||
dataType: 'json'
|
||||
});
|
||||
var callBack = this.reloadCallback.bind(this);
|
||||
return this._reloadCall.then(callBack, callBack);
|
||||
},
|
||||
|
||||
reloadCallback: function (result) {
|
||||
delete this._reloadCall;
|
||||
this.hideMask();
|
||||
|
||||
if (result.files) {
|
||||
this.setFiles(result.files.sort(this._sortComparator));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.Files.RecentFileList = RecentFileList;
|
||||
})(OCA);
|
||||
});
|
||||
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function(){
|
||||
var Semaphore = function(max) {
|
||||
var counter = 0;
|
||||
var waiting = [];
|
||||
|
||||
this.acquire = function() {
|
||||
if(counter < max) {
|
||||
counter++;
|
||||
return new Promise(function(resolve) { resolve(); });
|
||||
} else {
|
||||
return new Promise(function(resolve) { waiting.push(resolve); });
|
||||
}
|
||||
};
|
||||
|
||||
this.release = function() {
|
||||
counter--;
|
||||
if (waiting.length > 0 && counter < max) {
|
||||
counter++;
|
||||
var promise = waiting.shift();
|
||||
promise();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// needed on public share page to properly register this
|
||||
if (!OCA.Files) {
|
||||
OCA.Files = {};
|
||||
}
|
||||
OCA.Files.Semaphore = Semaphore;
|
||||
|
||||
})();
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function () {
|
||||
var SidebarPreviewManager = function (fileList) {
|
||||
this._fileList = fileList;
|
||||
this._previewHandlers = {};
|
||||
OC.Plugins.attach('OCA.Files.SidebarPreviewManager', this);
|
||||
};
|
||||
|
||||
SidebarPreviewManager.prototype = {
|
||||
addPreviewHandler: function (mime, handler) {
|
||||
this._previewHandlers[mime] = handler;
|
||||
},
|
||||
|
||||
getMimeTypePreviewHandler: function(mime) {
|
||||
var mimePart = mime.split('/').shift();
|
||||
if (this._previewHandlers[mime]) {
|
||||
return this._previewHandlers[mime];
|
||||
} else if (this._previewHandlers[mimePart]) {
|
||||
return this._previewHandlers[mimePart];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
getPreviewHandler: function (mime) {
|
||||
var mimetypeHandler = this.getMimeTypePreviewHandler(mime);
|
||||
if (mimetypeHandler) {
|
||||
return mimetypeHandler;
|
||||
} else {
|
||||
return this.fallbackPreview.bind(this);
|
||||
}
|
||||
},
|
||||
|
||||
loadPreview: function (model, $thumbnailDiv, $thumbnailContainer) {
|
||||
if (model.get('hasPreview') === false && this.getMimeTypePreviewHandler(model.get('mimetype')) === null) {
|
||||
var mimeIcon = OC.MimeType.getIconUrl(model.get('mimetype'));
|
||||
$thumbnailDiv.removeClass('icon-loading icon-32');
|
||||
$thumbnailContainer.removeClass('image'); //fall back to regular view
|
||||
$thumbnailDiv.css({
|
||||
'background-image': 'url("' + mimeIcon + '")'
|
||||
});
|
||||
} else {
|
||||
var handler = this.getPreviewHandler(model.get('mimetype'));
|
||||
var fallback = this.fallbackPreview.bind(this, model, $thumbnailDiv, $thumbnailContainer);
|
||||
handler(model, $thumbnailDiv, $thumbnailContainer, fallback);
|
||||
}
|
||||
},
|
||||
|
||||
// previews for images and mimetype icons
|
||||
fallbackPreview: function (model, $thumbnailDiv, $thumbnailContainer) {
|
||||
var isImage = model.isImage();
|
||||
var maxImageWidth = $thumbnailContainer.parent().width() + 50; // 50px for negative margins
|
||||
var maxImageHeight = maxImageWidth / (16 / 9);
|
||||
|
||||
var isLandscape = function (img) {
|
||||
return img.width > (img.height * 1.2);
|
||||
};
|
||||
|
||||
var isSmall = function (img) {
|
||||
return (img.width * 1.1) < (maxImageWidth * window.devicePixelRatio);
|
||||
};
|
||||
|
||||
var getTargetHeight = function (img) {
|
||||
var targetHeight = img.height / window.devicePixelRatio;
|
||||
if (targetHeight <= maxImageHeight) {
|
||||
targetHeight = maxImageHeight;
|
||||
}
|
||||
return targetHeight;
|
||||
};
|
||||
|
||||
var getTargetRatio = function (img) {
|
||||
var ratio = img.width / img.height;
|
||||
if (ratio > 16 / 9) {
|
||||
return ratio;
|
||||
} else {
|
||||
return 16 / 9;
|
||||
}
|
||||
};
|
||||
|
||||
this._fileList.lazyLoadPreview({
|
||||
fileId: model.get('id'),
|
||||
path: model.getFullPath(),
|
||||
mime: model.get('mimetype'),
|
||||
etag: model.get('etag'),
|
||||
y: maxImageHeight,
|
||||
x: maxImageWidth,
|
||||
a: 1,
|
||||
mode: 'cover',
|
||||
callback: function (previewUrl, img) {
|
||||
$thumbnailDiv.previewImg = previewUrl;
|
||||
|
||||
// as long as we only have the mimetype icon, we only save it in case there is no preview
|
||||
if (!img) {
|
||||
return;
|
||||
}
|
||||
$thumbnailDiv.removeClass('icon-loading icon-32');
|
||||
var targetHeight = getTargetHeight(img);
|
||||
$thumbnailContainer.addClass((isLandscape(img) && !isSmall(img)) ? 'landscape' : 'portrait');
|
||||
$thumbnailContainer.addClass('large');
|
||||
|
||||
// only set background when we have an actual preview
|
||||
// when we don't have a preview we show the mime icon in the error handler
|
||||
$thumbnailDiv.css({
|
||||
'background-image': 'url("' + previewUrl + '")',
|
||||
height: (targetHeight > maxImageHeight) ? 'auto' : targetHeight,
|
||||
'max-height': isSmall(img) ? targetHeight : null
|
||||
});
|
||||
|
||||
var targetRatio = getTargetRatio(img);
|
||||
$thumbnailDiv.find('.stretcher').css({
|
||||
'padding-bottom': (100 / targetRatio) + '%'
|
||||
});
|
||||
},
|
||||
error: function () {
|
||||
$thumbnailDiv.removeClass('icon-loading icon-32');
|
||||
$thumbnailContainer.removeClass('image'); //fall back to regular view
|
||||
$thumbnailDiv.css({
|
||||
'background-image': 'url("' + $thumbnailDiv.previewImg + '")'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
OCA.Files.SidebarPreviewManager = SidebarPreviewManager;
|
||||
})();
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
(function () {
|
||||
var SidebarPreview = function () {
|
||||
};
|
||||
|
||||
SidebarPreview.prototype = {
|
||||
attach: function (manager) {
|
||||
manager.addPreviewHandler('text', this.handlePreview.bind(this));
|
||||
},
|
||||
|
||||
handlePreview: function (model, $thumbnailDiv, $thumbnailContainer, fallback) {
|
||||
var previewWidth = $thumbnailContainer.parent().width() + 50; // 50px for negative margins
|
||||
var previewHeight = previewWidth / (16 / 9);
|
||||
|
||||
this.getFileContent(model.getFullPath()).then(function (content) {
|
||||
$thumbnailDiv.removeClass('icon-loading icon-32');
|
||||
$thumbnailContainer.addClass('large');
|
||||
$thumbnailContainer.addClass('text');
|
||||
var $textPreview = $('<pre></pre>').text(content);
|
||||
$thumbnailDiv.children('.stretcher').remove();
|
||||
$thumbnailDiv.append($textPreview);
|
||||
$thumbnailContainer.css("max-height", previewHeight);
|
||||
}, function () {
|
||||
fallback();
|
||||
});
|
||||
},
|
||||
|
||||
getFileContent: function (path) {
|
||||
return $.ajax({
|
||||
url: OC.linkToRemoteBase('files' + path),
|
||||
headers: {
|
||||
'Range': 'bytes=0-10240'
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
OC.Plugins.register('OCA.Files.SidebarPreviewManager', new SidebarPreview());
|
||||
})();
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/* global Handlebars */
|
||||
|
||||
(function (OCA) {
|
||||
|
||||
_.extend(OC.Files.Client, {
|
||||
PROPERTY_TAGS: '{' + OC.Files.Client.NS_OWNCLOUD + '}tags',
|
||||
PROPERTY_FAVORITE: '{' + OC.Files.Client.NS_OWNCLOUD + '}favorite'
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the icon class for the matching state
|
||||
*
|
||||
* @param {boolean} state true if starred, false otherwise
|
||||
* @return {string} icon class for star image
|
||||
*/
|
||||
function getStarIconClass (state) {
|
||||
return state ? 'icon-starred' : 'icon-star';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the star icon with the given state
|
||||
*
|
||||
* @param {boolean} state true if starred, false otherwise
|
||||
* @return {Object} jQuery object
|
||||
*/
|
||||
function renderStar (state) {
|
||||
return OCA.Files.Templates['favorite_mark']({
|
||||
isFavorite: state,
|
||||
altText: state ? t('files', 'Favorited') : t('files', 'Not favorited'),
|
||||
iconClass: getStarIconClass(state)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle star icon on favorite mark element
|
||||
*
|
||||
* @param {Object} $favoriteMarkEl favorite mark element
|
||||
* @param {boolean} state true if starred, false otherwise
|
||||
*/
|
||||
function toggleStar ($favoriteMarkEl, state) {
|
||||
$favoriteMarkEl.removeClass('icon-star icon-starred').addClass(getStarIconClass(state));
|
||||
$favoriteMarkEl.toggleClass('permanent', state);
|
||||
}
|
||||
|
||||
OCA.Files = OCA.Files || {};
|
||||
|
||||
/**
|
||||
* Extends the file actions and file list to include a favorite mark icon
|
||||
* and a favorite action in the file actions menu; it also adds "data-tags"
|
||||
* and "data-favorite" attributes to file elements.
|
||||
*
|
||||
* @namespace OCA.Files.TagsPlugin
|
||||
*/
|
||||
OCA.Files.TagsPlugin = {
|
||||
name: 'Tags',
|
||||
|
||||
allowedLists: [
|
||||
'files',
|
||||
'favorites',
|
||||
'systemtags',
|
||||
'shares.self',
|
||||
'shares.others',
|
||||
'shares.link'
|
||||
],
|
||||
|
||||
_extendFileActions: function (fileActions) {
|
||||
var self = this;
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'Favorite',
|
||||
displayName: function (context) {
|
||||
var $file = context.$file;
|
||||
var isFavorite = $file.data('favorite') === true;
|
||||
|
||||
if (isFavorite) {
|
||||
return t('files', 'Remove from favorites');
|
||||
}
|
||||
|
||||
// As it is currently not possible to provide a context for
|
||||
// the i18n strings "Add to favorites" was used instead of
|
||||
// "Favorite" to remove the ambiguity between verb and noun
|
||||
// when it is translated.
|
||||
return t('files', 'Add to favorites');
|
||||
},
|
||||
mime: 'all',
|
||||
order: -100,
|
||||
permissions: OC.PERMISSION_NONE,
|
||||
iconClass: function (fileName, context) {
|
||||
var $file = context.$file;
|
||||
var isFavorite = $file.data('favorite') === true;
|
||||
|
||||
if (isFavorite) {
|
||||
return 'icon-favorite';
|
||||
}
|
||||
|
||||
return 'icon-starred';
|
||||
},
|
||||
actionHandler: function (fileName, context) {
|
||||
var $favoriteMarkEl = context.$file.find('.favorite-mark');
|
||||
var $file = context.$file;
|
||||
var fileInfo = context.fileList.files[$file.index()];
|
||||
var dir = context.dir || context.fileList.getCurrentDirectory();
|
||||
var tags = $file.attr('data-tags');
|
||||
var isFile = $file.attr('data-type') === 'file';
|
||||
|
||||
if (_.isUndefined(tags)) {
|
||||
tags = '';
|
||||
}
|
||||
tags = tags.split('|');
|
||||
tags = _.without(tags, '');
|
||||
var isFavorite = tags.indexOf(OC.TAG_FAVORITE) >= 0;
|
||||
|
||||
// Fake Node object for vue compatibility
|
||||
const node = {
|
||||
type: isFile ? 'file' : 'folder',
|
||||
path: (dir + '/' + fileName).replace(/\/\/+/g, '/'),
|
||||
root: '/files/' + OC.getCurrentUser().uid
|
||||
}
|
||||
|
||||
if (isFavorite) {
|
||||
// remove tag from list
|
||||
tags = _.without(tags, OC.TAG_FAVORITE);
|
||||
// vue compatibility
|
||||
window._nc_event_bus.emit('files:favorites:removed', node)
|
||||
} else {
|
||||
tags.push(OC.TAG_FAVORITE);
|
||||
// vue compatibility
|
||||
window._nc_event_bus.emit('files:favorites:added', node)
|
||||
}
|
||||
|
||||
// pre-toggle the star
|
||||
toggleStar($favoriteMarkEl, !isFavorite);
|
||||
|
||||
context.fileInfoModel.trigger('busy', context.fileInfoModel, true);
|
||||
|
||||
self.applyFileTags(
|
||||
dir + '/' + fileName,
|
||||
tags,
|
||||
$favoriteMarkEl,
|
||||
isFavorite
|
||||
).then(function (result) {
|
||||
context.fileInfoModel.trigger('busy', context.fileInfoModel, false);
|
||||
// response from server should contain updated tags
|
||||
var newTags = result.tags;
|
||||
if (_.isUndefined(newTags)) {
|
||||
newTags = tags;
|
||||
}
|
||||
context.fileInfoModel.set({
|
||||
'tags': newTags,
|
||||
'favorite': !isFavorite
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_extendFileList: function (fileList) {
|
||||
// extend row prototype
|
||||
var oldCreateRow = fileList._createRow;
|
||||
fileList._createRow = function (fileData) {
|
||||
var $tr = oldCreateRow.apply(this, arguments);
|
||||
var isFavorite = false;
|
||||
if (fileData.tags) {
|
||||
$tr.attr('data-tags', fileData.tags.join('|'));
|
||||
if (fileData.tags.indexOf(OC.TAG_FAVORITE) >= 0) {
|
||||
$tr.attr('data-favorite', true);
|
||||
isFavorite = true;
|
||||
}
|
||||
}
|
||||
var $icon = $(renderStar(isFavorite));
|
||||
$tr.find('td.filename .thumbnail').append($icon);
|
||||
return $tr;
|
||||
};
|
||||
var oldElementToFile = fileList.elementToFile;
|
||||
fileList.elementToFile = function ($el) {
|
||||
var fileInfo = oldElementToFile.apply(this, arguments);
|
||||
var tags = $el.attr('data-tags');
|
||||
if (_.isUndefined(tags)) {
|
||||
tags = '';
|
||||
}
|
||||
tags = tags.split('|');
|
||||
tags = _.without(tags, '');
|
||||
fileInfo.tags = tags;
|
||||
return fileInfo;
|
||||
};
|
||||
|
||||
var oldGetWebdavProperties = fileList._getWebdavProperties;
|
||||
fileList._getWebdavProperties = function () {
|
||||
var props = oldGetWebdavProperties.apply(this, arguments);
|
||||
props.push(OC.Files.Client.PROPERTY_TAGS);
|
||||
props.push(OC.Files.Client.PROPERTY_FAVORITE);
|
||||
return props;
|
||||
};
|
||||
|
||||
fileList.filesClient.addFileInfoParser(function (response) {
|
||||
var data = {};
|
||||
var props = response.propStat[0].properties;
|
||||
var tags = props[OC.Files.Client.PROPERTY_TAGS];
|
||||
var favorite = props[OC.Files.Client.PROPERTY_FAVORITE];
|
||||
if (tags && tags.length) {
|
||||
tags = _.chain(tags).filter(function (xmlvalue) {
|
||||
return (xmlvalue.namespaceURI === OC.Files.Client.NS_OWNCLOUD && xmlvalue.nodeName.split(':')[1] === 'tag');
|
||||
}).map(function (xmlvalue) {
|
||||
return xmlvalue.textContent || xmlvalue.text;
|
||||
}).value();
|
||||
}
|
||||
if (tags) {
|
||||
data.tags = tags;
|
||||
}
|
||||
if (favorite && parseInt(favorite, 10) !== 0) {
|
||||
data.tags = data.tags || [];
|
||||
data.tags.push(OC.TAG_FAVORITE);
|
||||
}
|
||||
return data;
|
||||
});
|
||||
},
|
||||
|
||||
attach: function (fileList) {
|
||||
if (this.allowedLists.indexOf(fileList.id) < 0) {
|
||||
return;
|
||||
}
|
||||
this._extendFileActions(fileList.fileActions);
|
||||
this._extendFileList(fileList);
|
||||
},
|
||||
|
||||
/**
|
||||
* Replaces the given files' tags with the specified ones.
|
||||
*
|
||||
* @param {String} fileName path to the file or folder to tag
|
||||
* @param {Array.<String>} tagNames array of tag names
|
||||
* @param {Object} $favoriteMarkEl favorite mark element
|
||||
* @param {boolean} isFavorite Was the item favorited before
|
||||
*/
|
||||
applyFileTags: function (fileName, tagNames, $favoriteMarkEl, isFavorite) {
|
||||
var encodedPath = OC.encodePath(fileName);
|
||||
while (encodedPath[0] === '/') {
|
||||
encodedPath = encodedPath.substr(1);
|
||||
}
|
||||
return $.ajax({
|
||||
url: OC.generateUrl('/apps/files/api/v1/files/') + encodedPath,
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({
|
||||
tags: tagNames || []
|
||||
}),
|
||||
dataType: 'json',
|
||||
type: 'POST'
|
||||
}).fail(function (response) {
|
||||
var message = '';
|
||||
// show message if it is available
|
||||
if (response.responseJSON && response.responseJSON.message) {
|
||||
message = ': ' + response.responseJSON.message;
|
||||
}
|
||||
OC.Notification.show(t('files', 'An error occurred while trying to update the tags' + message), {type: 'error'});
|
||||
toggleStar($favoriteMarkEl, isFavorite);
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
(OCA);
|
||||
|
||||
OC.Plugins.register('OCA.Files.FileList', OCA.Files.TagsPlugin);
|
||||
|
|
@ -1,430 +0,0 @@
|
|||
(function() {
|
||||
var template = Handlebars.template, templates = OCA.Files.Templates = OCA.Files.Templates || {};
|
||||
templates['detailsview'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<ul class=\"tabHeaders\">\n"
|
||||
+ ((stack1 = lookupProperty(helpers,"each").call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? lookupProperty(depth0,"tabHeaders") : depth0),{"name":"each","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":4,"column":1},"end":{"line":9,"column":10}}})) != null ? stack1 : "")
|
||||
+ "</ul>\n";
|
||||
},"2":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return " <li class=\"tabHeader\" data-tabid=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"tabId") || (depth0 != null ? lookupProperty(depth0,"tabId") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"tabId","hash":{},"data":data,"loc":{"start":{"line":5,"column":35},"end":{"line":5,"column":44}}}) : helper)))
|
||||
+ "\" tabindex=\"0\">\n "
|
||||
+ ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"tabIcon") : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":6,"column":5},"end":{"line":6,"column":65}}})) != null ? stack1 : "")
|
||||
+ "\n <a href=\"#\" tabindex=\"-1\">"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"label") || (depth0 != null ? lookupProperty(depth0,"label") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"label","hash":{},"data":data,"loc":{"start":{"line":7,"column":28},"end":{"line":7,"column":37}}}) : helper)))
|
||||
+ "</a>\n </li>\n";
|
||||
},"3":function(container,depth0,helpers,partials,data) {
|
||||
var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<span class=\"icon "
|
||||
+ container.escapeExpression(((helper = (helper = lookupProperty(helpers,"tabIcon") || (depth0 != null ? lookupProperty(depth0,"tabIcon") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"tabIcon","hash":{},"data":data,"loc":{"start":{"line":6,"column":38},"end":{"line":6,"column":49}}}) : helper)))
|
||||
+ "\"></span>";
|
||||
},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<div class=\"detailFileInfoContainer\"></div>\n"
|
||||
+ ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"tabHeaders") : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":2,"column":0},"end":{"line":11,"column":7}}})) != null ? stack1 : "")
|
||||
+ "<div class=\"tabsContainer\"></div>\n<a class=\"close icon-close\" href=\"#\"><span class=\"hidden-visually\">"
|
||||
+ container.escapeExpression(((helper = (helper = lookupProperty(helpers,"closeLabel") || (depth0 != null ? lookupProperty(depth0,"closeLabel") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(alias1,{"name":"closeLabel","hash":{},"data":data,"loc":{"start":{"line":13,"column":67},"end":{"line":13,"column":81}}}) : helper)))
|
||||
+ "</span></a>\n";
|
||||
},"useData":true});
|
||||
templates['favorite_mark'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||
return "permanent";
|
||||
},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
}, buffer =
|
||||
"<div class=\"favorite-mark ";
|
||||
stack1 = ((helper = (helper = lookupProperty(helpers,"isFavorite") || (depth0 != null ? lookupProperty(depth0,"isFavorite") : depth0)) != null ? helper : alias2),(options={"name":"isFavorite","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":1,"column":26},"end":{"line":1,"column":65}}}),(typeof helper === alias3 ? helper.call(alias1,options) : helper));
|
||||
if (!lookupProperty(helpers,"isFavorite")) { stack1 = container.hooks.blockHelperMissing.call(depth0,stack1,options)}
|
||||
if (stack1 != null) { buffer += stack1; }
|
||||
return buffer + "\">\n <span class=\"icon "
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"iconClass") || (depth0 != null ? lookupProperty(depth0,"iconClass") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"iconClass","hash":{},"data":data,"loc":{"start":{"line":2,"column":19},"end":{"line":2,"column":32}}}) : helper)))
|
||||
+ "\" />\n <span class=\"hidden-visually\">"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"altText") || (depth0 != null ? lookupProperty(depth0,"altText") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"altText","hash":{},"data":data,"loc":{"start":{"line":3,"column":31},"end":{"line":3,"column":42}}}) : helper)))
|
||||
+ "</span>\n</div>\n";
|
||||
},"useData":true});
|
||||
templates['file_action_trigger'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return " <img class=\"svg\" alt=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"altText") || (depth0 != null ? lookupProperty(depth0,"altText") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"altText","hash":{},"data":data,"loc":{"start":{"line":3,"column":24},"end":{"line":3,"column":35}}}) : helper)))
|
||||
+ "\" src=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"icon") || (depth0 != null ? lookupProperty(depth0,"icon") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"icon","hash":{},"data":data,"loc":{"start":{"line":3,"column":42},"end":{"line":3,"column":50}}}) : helper)))
|
||||
+ "\" />\n";
|
||||
},"3":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}), lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"iconClass") : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":5,"column":2},"end":{"line":7,"column":9}}})) != null ? stack1 : "")
|
||||
+ ((stack1 = lookupProperty(helpers,"unless").call(alias1,(depth0 != null ? lookupProperty(depth0,"hasDisplayName") : depth0),{"name":"unless","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":8,"column":2},"end":{"line":10,"column":13}}})) != null ? stack1 : "");
|
||||
},"4":function(container,depth0,helpers,partials,data) {
|
||||
var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return " <span class=\"icon "
|
||||
+ container.escapeExpression(((helper = (helper = lookupProperty(helpers,"iconClass") || (depth0 != null ? lookupProperty(depth0,"iconClass") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"iconClass","hash":{},"data":data,"loc":{"start":{"line":6,"column":21},"end":{"line":6,"column":34}}}) : helper)))
|
||||
+ "\"></span>\n";
|
||||
},"6":function(container,depth0,helpers,partials,data) {
|
||||
var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return " <span class=\"hidden-visually\">"
|
||||
+ container.escapeExpression(((helper = (helper = lookupProperty(helpers,"altText") || (depth0 != null ? lookupProperty(depth0,"altText") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"altText","hash":{},"data":data,"loc":{"start":{"line":9,"column":33},"end":{"line":9,"column":44}}}) : helper)))
|
||||
+ "</span>\n";
|
||||
},"8":function(container,depth0,helpers,partials,data) {
|
||||
var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<span> "
|
||||
+ container.escapeExpression(((helper = (helper = lookupProperty(helpers,"displayName") || (depth0 != null ? lookupProperty(depth0,"displayName") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"displayName","hash":{},"data":data,"loc":{"start":{"line":12,"column":27},"end":{"line":12,"column":42}}}) : helper)))
|
||||
+ "</span>";
|
||||
},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<a class=\"action action-"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"nameLowerCase") || (depth0 != null ? lookupProperty(depth0,"nameLowerCase") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"nameLowerCase","hash":{},"data":data,"loc":{"start":{"line":1,"column":24},"end":{"line":1,"column":41}}}) : helper)))
|
||||
+ "\" href=\"#\" data-action=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":1,"column":65},"end":{"line":1,"column":73}}}) : helper)))
|
||||
+ "\">\n"
|
||||
+ ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"icon") : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.program(3, data, 0),"data":data,"loc":{"start":{"line":2,"column":1},"end":{"line":11,"column":8}}})) != null ? stack1 : "")
|
||||
+ " "
|
||||
+ ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"displayName") : depth0),{"name":"if","hash":{},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":12,"column":1},"end":{"line":12,"column":56}}})) != null ? stack1 : "")
|
||||
+ "\n</a>\n";
|
||||
},"useData":true});
|
||||
templates['fileactionsmenu'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return " <li class=\""
|
||||
+ ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"inline") : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":3,"column":13},"end":{"line":3,"column":40}}})) != null ? stack1 : "")
|
||||
+ " action-"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"nameLowerCase") || (depth0 != null ? lookupProperty(depth0,"nameLowerCase") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"nameLowerCase","hash":{},"data":data,"loc":{"start":{"line":3,"column":48},"end":{"line":3,"column":65}}}) : helper)))
|
||||
+ "-container\">\n <a href=\"#\" class=\"menuitem action action-"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"nameLowerCase") || (depth0 != null ? lookupProperty(depth0,"nameLowerCase") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"nameLowerCase","hash":{},"data":data,"loc":{"start":{"line":4,"column":45},"end":{"line":4,"column":62}}}) : helper)))
|
||||
+ " permanent\" data-action=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":4,"column":87},"end":{"line":4,"column":95}}}) : helper)))
|
||||
+ "\">\n "
|
||||
+ ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"icon") : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.program(6, data, 0),"data":data,"loc":{"start":{"line":5,"column":4},"end":{"line":12,"column":11}}})) != null ? stack1 : "")
|
||||
+ " <span>"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"displayName") || (depth0 != null ? lookupProperty(depth0,"displayName") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"displayName","hash":{},"data":data,"loc":{"start":{"line":13,"column":10},"end":{"line":13,"column":25}}}) : helper)))
|
||||
+ "</span>\n </a>\n </li>\n";
|
||||
},"2":function(container,depth0,helpers,partials,data) {
|
||||
return "hidden";
|
||||
},"4":function(container,depth0,helpers,partials,data) {
|
||||
var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<img class=\"icon\" src=\""
|
||||
+ container.escapeExpression(((helper = (helper = lookupProperty(helpers,"icon") || (depth0 != null ? lookupProperty(depth0,"icon") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"icon","hash":{},"data":data,"loc":{"start":{"line":5,"column":39},"end":{"line":5,"column":47}}}) : helper)))
|
||||
+ "\"/>\n";
|
||||
},"6":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return ((stack1 = lookupProperty(helpers,"if").call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? lookupProperty(depth0,"iconClass") : depth0),{"name":"if","hash":{},"fn":container.program(7, data, 0),"inverse":container.program(9, data, 0),"data":data,"loc":{"start":{"line":7,"column":5},"end":{"line":11,"column":12}}})) != null ? stack1 : "");
|
||||
},"7":function(container,depth0,helpers,partials,data) {
|
||||
var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return " <span class=\"icon "
|
||||
+ container.escapeExpression(((helper = (helper = lookupProperty(helpers,"iconClass") || (depth0 != null ? lookupProperty(depth0,"iconClass") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"iconClass","hash":{},"data":data,"loc":{"start":{"line":8,"column":24},"end":{"line":8,"column":37}}}) : helper)))
|
||||
+ "\"></span>\n";
|
||||
},"9":function(container,depth0,helpers,partials,data) {
|
||||
return " <span class=\"no-icon\"></span>\n";
|
||||
},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<ul>\n"
|
||||
+ ((stack1 = lookupProperty(helpers,"each").call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? lookupProperty(depth0,"items") : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":2,"column":1},"end":{"line":16,"column":10}}})) != null ? stack1 : "")
|
||||
+ "</ul>\n";
|
||||
},"useData":true});
|
||||
templates['filemultiselectmenu'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return " <li class=\"item-"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":3,"column":18},"end":{"line":3,"column":26}}}) : helper)))
|
||||
+ "\">\n <a href=\"#\" class=\"menuitem action "
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":4,"column":38},"end":{"line":4,"column":46}}}) : helper)))
|
||||
+ " permanent\" data-action=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":4,"column":71},"end":{"line":4,"column":79}}}) : helper)))
|
||||
+ "\">\n"
|
||||
+ ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"iconClass") : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.program(4, data, 0),"data":data,"loc":{"start":{"line":5,"column":4},"end":{"line":9,"column":11}}})) != null ? stack1 : "")
|
||||
+ " <span class=\"label\">"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"displayName") || (depth0 != null ? lookupProperty(depth0,"displayName") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"displayName","hash":{},"data":data,"loc":{"start":{"line":10,"column":24},"end":{"line":10,"column":39}}}) : helper)))
|
||||
+ "</span>\n </a>\n </li>\n";
|
||||
},"2":function(container,depth0,helpers,partials,data) {
|
||||
var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return " <span class=\"icon "
|
||||
+ container.escapeExpression(((helper = (helper = lookupProperty(helpers,"iconClass") || (depth0 != null ? lookupProperty(depth0,"iconClass") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"iconClass","hash":{},"data":data,"loc":{"start":{"line":6,"column":23},"end":{"line":6,"column":36}}}) : helper)))
|
||||
+ "\"></span>\n";
|
||||
},"4":function(container,depth0,helpers,partials,data) {
|
||||
return " <span class=\"no-icon\"></span>\n";
|
||||
},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<ul>\n"
|
||||
+ ((stack1 = lookupProperty(helpers,"each").call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? lookupProperty(depth0,"items") : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":2,"column":1},"end":{"line":13,"column":10}}})) != null ? stack1 : "")
|
||||
+ "</ul>\n";
|
||||
},"useData":true});
|
||||
templates['filesummary'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<span class=\"info\">\n <span class=\"dirinfo\"></span>\n <span class=\"connector\">"
|
||||
+ container.escapeExpression(((helper = (helper = lookupProperty(helpers,"connectorLabel") || (depth0 != null ? lookupProperty(depth0,"connectorLabel") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"connectorLabel","hash":{},"data":data,"loc":{"start":{"line":3,"column":25},"end":{"line":3,"column":43}}}) : helper)))
|
||||
+ "</span>\n <span class=\"fileinfo\"></span>\n <span class=\"hiddeninfo\"></span>\n <span class=\"filter\"></span>\n</span>\n";
|
||||
},"useData":true});
|
||||
templates['mainfileinfodetailsview'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return " <a href=\"#\" class=\"action action-favorite favorite permanent\">\n <span class=\"icon "
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"starClass") || (depth0 != null ? lookupProperty(depth0,"starClass") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"starClass","hash":{},"data":data,"loc":{"start":{"line":13,"column":22},"end":{"line":13,"column":35}}}) : helper)))
|
||||
+ "\" title=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"starAltText") || (depth0 != null ? lookupProperty(depth0,"starAltText") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"starAltText","hash":{},"data":data,"loc":{"start":{"line":13,"column":44},"end":{"line":13,"column":59}}}) : helper)))
|
||||
+ "\"></span>\n </a>\n";
|
||||
},"3":function(container,depth0,helpers,partials,data) {
|
||||
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<span class=\"size\" title=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"altSize") || (depth0 != null ? lookupProperty(depth0,"altSize") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"altSize","hash":{},"data":data,"loc":{"start":{"line":16,"column":43},"end":{"line":16,"column":54}}}) : helper)))
|
||||
+ "\">"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"size") || (depth0 != null ? lookupProperty(depth0,"size") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"size","hash":{},"data":data,"loc":{"start":{"line":16,"column":56},"end":{"line":16,"column":64}}}) : helper)))
|
||||
+ "</span>, ";
|
||||
},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<div class=\"thumbnailContainer\"><a href=\"#\" class=\"thumbnail action-default\"><div class=\"stretcher\"></div></a></div>\n<div class=\"file-details-container\">\n <div class=\"fileName\">\n <h3 title=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":4,"column":13},"end":{"line":4,"column":21}}}) : helper)))
|
||||
+ "\" class=\"ellipsis\">"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":4,"column":40},"end":{"line":4,"column":48}}}) : helper)))
|
||||
+ "</h3>\n <a class=\"permalink\" href=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"permalink") || (depth0 != null ? lookupProperty(depth0,"permalink") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"permalink","hash":{},"data":data,"loc":{"start":{"line":5,"column":29},"end":{"line":5,"column":42}}}) : helper)))
|
||||
+ "\" title=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"permalinkTitle") || (depth0 != null ? lookupProperty(depth0,"permalinkTitle") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"permalinkTitle","hash":{},"data":data,"loc":{"start":{"line":5,"column":51},"end":{"line":5,"column":69}}}) : helper)))
|
||||
+ "\" data-clipboard-text=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"permalink") || (depth0 != null ? lookupProperty(depth0,"permalink") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"permalink","hash":{},"data":data,"loc":{"start":{"line":5,"column":92},"end":{"line":5,"column":105}}}) : helper)))
|
||||
+ "\">\n <span class=\"icon icon-clippy\"></span>\n <span class=\"hidden-visually\">"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"permalinkTitle") || (depth0 != null ? lookupProperty(depth0,"permalinkTitle") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"permalinkTitle","hash":{},"data":data,"loc":{"start":{"line":7,"column":33},"end":{"line":7,"column":51}}}) : helper)))
|
||||
+ "</span>\n </a>\n </div>\n <div class=\"file-details ellipsis\">\n"
|
||||
+ ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"hasFavoriteAction") : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":11,"column":2},"end":{"line":15,"column":9}}})) != null ? stack1 : "")
|
||||
+ " "
|
||||
+ ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"hasSize") : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":16,"column":2},"end":{"line":16,"column":80}}})) != null ? stack1 : "")
|
||||
+ "<span class=\"date live-relative-timestamp\" data-timestamp=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"timestamp") || (depth0 != null ? lookupProperty(depth0,"timestamp") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"timestamp","hash":{},"data":data,"loc":{"start":{"line":16,"column":139},"end":{"line":16,"column":152}}}) : helper)))
|
||||
+ "\" title=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"altDate") || (depth0 != null ? lookupProperty(depth0,"altDate") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"altDate","hash":{},"data":data,"loc":{"start":{"line":16,"column":161},"end":{"line":16,"column":172}}}) : helper)))
|
||||
+ "\">"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"date") || (depth0 != null ? lookupProperty(depth0,"date") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"date","hash":{},"data":data,"loc":{"start":{"line":16,"column":174},"end":{"line":16,"column":182}}}) : helper)))
|
||||
+ "</span>\n </div>\n</div>\n<div class=\"hidden permalink-field\">\n <input type=\"text\" value=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"permalink") || (depth0 != null ? lookupProperty(depth0,"permalink") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"permalink","hash":{},"data":data,"loc":{"start":{"line":20,"column":27},"end":{"line":20,"column":40}}}) : helper)))
|
||||
+ "\" placeholder=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"permalinkTitle") || (depth0 != null ? lookupProperty(depth0,"permalinkTitle") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"permalinkTitle","hash":{},"data":data,"loc":{"start":{"line":20,"column":55},"end":{"line":20,"column":73}}}) : helper)))
|
||||
+ "\" readonly=\"readonly\"/>\n</div>\n";
|
||||
},"useData":true});
|
||||
templates['newfilemenu'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return " <li>\n <a href=\"#\" class=\"menuitem\" data-templatename=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"templateName") || (depth0 != null ? lookupProperty(depth0,"templateName") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"templateName","hash":{},"data":data,"loc":{"start":{"line":7,"column":51},"end":{"line":7,"column":67}}}) : helper)))
|
||||
+ "\" data-filetype=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"fileType") || (depth0 != null ? lookupProperty(depth0,"fileType") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"fileType","hash":{},"data":data,"loc":{"start":{"line":7,"column":84},"end":{"line":7,"column":96}}}) : helper)))
|
||||
+ "\" data-action=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"id") || (depth0 != null ? lookupProperty(depth0,"id") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"id","hash":{},"data":data,"loc":{"start":{"line":7,"column":111},"end":{"line":7,"column":117}}}) : helper)))
|
||||
+ "\" data-action-label=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"actionLabel") || (depth0 != null ? lookupProperty(depth0,"actionLabel") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"actionLabel","hash":{},"data":data,"loc":{"start":{"line":7,"column":138},"end":{"line":7,"column":153}}}) : helper)))
|
||||
+ "\"><span class=\"icon "
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"iconClass") || (depth0 != null ? lookupProperty(depth0,"iconClass") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"iconClass","hash":{},"data":data,"loc":{"start":{"line":7,"column":173},"end":{"line":7,"column":186}}}) : helper)))
|
||||
+ " svg\"></span><span class=\"displayname\">"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"displayName") || (depth0 != null ? lookupProperty(depth0,"displayName") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"displayName","hash":{},"data":data,"loc":{"start":{"line":7,"column":225},"end":{"line":7,"column":240}}}) : helper)))
|
||||
+ "</span></a>\n </li>\n";
|
||||
},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<ul>\n <li>\n <label for=\"file_upload_start\" class=\"menuitem\" data-action=\"upload\" title=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"uploadMaxHumanFilesize") || (depth0 != null ? lookupProperty(depth0,"uploadMaxHumanFilesize") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"uploadMaxHumanFilesize","hash":{},"data":data,"loc":{"start":{"line":3,"column":78},"end":{"line":3,"column":104}}}) : helper)))
|
||||
+ "\" tabindex=\"0\"><span class=\"svg icon icon-upload\"></span><span class=\"displayname\">"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"uploadLabel") || (depth0 != null ? lookupProperty(depth0,"uploadLabel") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"uploadLabel","hash":{},"data":data,"loc":{"start":{"line":3,"column":187},"end":{"line":3,"column":202}}}) : helper)))
|
||||
+ "</span></label>\n </li>\n"
|
||||
+ ((stack1 = lookupProperty(helpers,"each").call(alias1,(depth0 != null ? lookupProperty(depth0,"items") : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":5,"column":1},"end":{"line":9,"column":10}}})) != null ? stack1 : "")
|
||||
+ "</ul>\n";
|
||||
},"useData":true});
|
||||
templates['newfilemenu_filename_form'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<form class=\"filenameform\">\n <input id=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"cid") || (depth0 != null ? lookupProperty(depth0,"cid") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data,"loc":{"start":{"line":2,"column":12},"end":{"line":2,"column":19}}}) : helper)))
|
||||
+ "-input-"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"fileType") || (depth0 != null ? lookupProperty(depth0,"fileType") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"fileType","hash":{},"data":data,"loc":{"start":{"line":2,"column":26},"end":{"line":2,"column":38}}}) : helper)))
|
||||
+ "\" type=\"text\" value=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"fileName") || (depth0 != null ? lookupProperty(depth0,"fileName") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"fileName","hash":{},"data":data,"loc":{"start":{"line":2,"column":59},"end":{"line":2,"column":71}}}) : helper)))
|
||||
+ "\" autocomplete=\"off\" autocapitalize=\"off\">\n <input type=\"submit\" value=\" \" class=\"icon-confirm\" aria-label=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"actionLabel") || (depth0 != null ? lookupProperty(depth0,"actionLabel") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"actionLabel","hash":{},"data":data,"loc":{"start":{"line":3,"column":65},"end":{"line":3,"column":80}}}) : helper)))
|
||||
+ "\" />\n</form>\n";
|
||||
},"useData":true});
|
||||
templates['operationprogressbar'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<div id=\"uploadprogressbar\">\n <em class=\"label outer\" style=\"display:none\"></em>\n</div>\n<button class=\"stop icon-close\" style=\"display:none\">\n <span class=\"hidden-visually\">"
|
||||
+ container.escapeExpression(((helper = (helper = lookupProperty(helpers,"textCancelButton") || (depth0 != null ? lookupProperty(depth0,"textCancelButton") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"textCancelButton","hash":{},"data":data,"loc":{"start":{"line":5,"column":31},"end":{"line":5,"column":51}}}) : helper)))
|
||||
+ "</span>\n</button>\n";
|
||||
},"useData":true});
|
||||
templates['operationprogressbarlabel'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<em class=\"label\">\n <span class=\"desktop\">"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"textDesktop") || (depth0 != null ? lookupProperty(depth0,"textDesktop") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"textDesktop","hash":{},"data":data,"loc":{"start":{"line":2,"column":23},"end":{"line":2,"column":38}}}) : helper)))
|
||||
+ "</span>\n <span class=\"mobile\">"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"textMobile") || (depth0 != null ? lookupProperty(depth0,"textMobile") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"textMobile","hash":{},"data":data,"loc":{"start":{"line":3,"column":22},"end":{"line":3,"column":36}}}) : helper)))
|
||||
+ "</span>\n</em>\n";
|
||||
},"useData":true});
|
||||
templates['template_addbutton'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return "<a href=\"#\" class=\"button new\" aria-label=\""
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"addLongText") || (depth0 != null ? lookupProperty(depth0,"addLongText") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"addLongText","hash":{},"data":data,"loc":{"start":{"line":1,"column":43},"end":{"line":1,"column":58}}}) : helper)))
|
||||
+ "\">\n <span class=\"icon "
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"iconClass") || (depth0 != null ? lookupProperty(depth0,"iconClass") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"iconClass","hash":{},"data":data,"loc":{"start":{"line":2,"column":19},"end":{"line":2,"column":32}}}) : helper)))
|
||||
+ "\"></span>\n <span>"
|
||||
+ alias4(((helper = (helper = lookupProperty(helpers,"addText") || (depth0 != null ? lookupProperty(depth0,"addText") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"addText","hash":{},"data":data,"loc":{"start":{"line":3,"column":7},"end":{"line":3,"column":18}}}) : helper)))
|
||||
+ "</span>\n</a>\n";
|
||||
},"useData":true});
|
||||
})();
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<div class="detailFileInfoContainer"></div>
|
||||
{{#if tabHeaders}}
|
||||
<ul class="tabHeaders">
|
||||
{{#each tabHeaders}}
|
||||
<li class="tabHeader" data-tabid="{{tabId}}" tabindex="0">
|
||||
{{#if tabIcon}}<span class="icon {{tabIcon}}"></span>{{/if}}
|
||||
<a href="#" tabindex="-1">{{label}}</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
<div class="tabsContainer"></div>
|
||||
<a class="close icon-close" href="#"><span class="hidden-visually">{{closeLabel}}</span></a>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<div class="favorite-mark {{#isFavorite}}permanent{{/isFavorite}}">
|
||||
<span class="icon {{iconClass}}" />
|
||||
<span class="hidden-visually">{{altText}}</span>
|
||||
</div>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<a class="action action-{{nameLowerCase}}" href="#" data-action="{{name}}">
|
||||
{{#if icon}}
|
||||
<img class="svg" alt="{{altText}}" src="{{icon}}" />
|
||||
{{else}}
|
||||
{{#if iconClass}}
|
||||
<span class="icon {{iconClass}}"></span>
|
||||
{{/if}}
|
||||
{{#unless hasDisplayName}}
|
||||
<span class="hidden-visually">{{altText}}</span>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
{{#if displayName}}<span> {{displayName}}</span>{{/if}}
|
||||
</a>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<ul>
|
||||
{{#each items}}
|
||||
<li class="{{#if inline}}hidden{{/if}} action-{{nameLowerCase}}-container">
|
||||
<a href="#" class="menuitem action action-{{nameLowerCase}} permanent" data-action="{{name}}">
|
||||
{{#if icon}}<img class="icon" src="{{icon}}"/>
|
||||
{{else}}
|
||||
{{#if iconClass}}
|
||||
<span class="icon {{iconClass}}"></span>
|
||||
{{else}}
|
||||
<span class="no-icon"></span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
<span>{{displayName}}</span>
|
||||
</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<ul>
|
||||
{{#each items}}
|
||||
<li class="item-{{name}}">
|
||||
<a href="#" class="menuitem action {{name}} permanent" data-action="{{name}}">
|
||||
{{#if iconClass}}
|
||||
<span class="icon {{iconClass}}"></span>
|
||||
{{else}}
|
||||
<span class="no-icon"></span>
|
||||
{{/if}}
|
||||
<span class="label">{{displayName}}</span>
|
||||
</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<span class="info">
|
||||
<span class="dirinfo"></span>
|
||||
<span class="connector">{{connectorLabel}}</span>
|
||||
<span class="fileinfo"></span>
|
||||
<span class="hiddeninfo"></span>
|
||||
<span class="filter"></span>
|
||||
</span>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<div class="thumbnailContainer"><a href="#" class="thumbnail action-default"><div class="stretcher"></div></a></div>
|
||||
<div class="file-details-container">
|
||||
<div class="fileName">
|
||||
<h3 title="{{name}}" class="ellipsis">{{name}}</h3>
|
||||
<a class="permalink" href="{{permalink}}" title="{{permalinkTitle}}" data-clipboard-text="{{permalink}}">
|
||||
<span class="icon icon-clippy"></span>
|
||||
<span class="hidden-visually">{{permalinkTitle}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="file-details ellipsis">
|
||||
{{#if hasFavoriteAction}}
|
||||
<a href="#" class="action action-favorite favorite permanent">
|
||||
<span class="icon {{starClass}}" title="{{starAltText}}"></span>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if hasSize}}<span class="size" title="{{altSize}}">{{size}}</span>, {{/if}}<span class="date live-relative-timestamp" data-timestamp="{{timestamp}}" title="{{altDate}}">{{date}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden permalink-field">
|
||||
<input type="text" value="{{permalink}}" placeholder="{{permalinkTitle}}" readonly="readonly"/>
|
||||
</div>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<ul>
|
||||
<li>
|
||||
<label for="file_upload_start" class="menuitem" data-action="upload" title="{{uploadMaxHumanFilesize}}" tabindex="0"><span class="svg icon icon-upload"></span><span class="displayname">{{uploadLabel}}</span></label>
|
||||
</li>
|
||||
{{#each items}}
|
||||
<li>
|
||||
<a href="#" class="menuitem" data-templatename="{{templateName}}" data-filetype="{{fileType}}" data-action="{{id}}" data-action-label="{{actionLabel}}"><span class="icon {{iconClass}} svg"></span><span class="displayname">{{displayName}}</span></a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<form class="filenameform">
|
||||
<input id="{{cid}}-input-{{fileType}}" type="text" value="{{fileName}}" autocomplete="off" autocapitalize="off">
|
||||
<input type="submit" value=" " class="icon-confirm" aria-label="{{actionLabel}}" />
|
||||
</form>
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
<div id="uploadprogressbar">
|
||||
<em class="label outer" style="display:none"></em>
|
||||
</div>
|
||||
<button class="stop icon-close" style="display:none">
|
||||
<span class="hidden-visually">{{textCancelButton}}</span>
|
||||
</button>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<em class="label">
|
||||
<span class="desktop">{{textDesktop}}</span>
|
||||
<span class="mobile">{{textMobile}}</span>
|
||||
</em>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<a href="#" class="button new" aria-label="{{addLongText}}">
|
||||
<span class="icon {{iconClass}}"></span>
|
||||
<span>{{addText}}</span>
|
||||
</a>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2013-2014 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
function Upload(fileSelector) {
|
||||
if ($.support.xhrFileUpload) {
|
||||
return new XHRUpload(fileSelector.target.files);
|
||||
} else {
|
||||
return new FormUpload(fileSelector);
|
||||
}
|
||||
}
|
||||
Upload.target = OC.filePath('files', 'ajax', 'upload.php');
|
||||
|
|
@ -139,7 +139,6 @@ class ViewController extends Controller {
|
|||
|
||||
// Load the files we need
|
||||
\OCP\Util::addInitScript('files', 'init');
|
||||
\OCP\Util::addStyle('files', 'merged');
|
||||
\OCP\Util::addScript('files', 'main');
|
||||
|
||||
$userId = $this->userSession->getUser()->getUID();
|
||||
|
|
|
|||
|
|
@ -22,8 +22,5 @@ class LoadSidebarListener implements IEventListener {
|
|||
}
|
||||
|
||||
Util::addScript(Application::APP_ID, 'sidebar');
|
||||
// needed by the Sidebar legacy tabs
|
||||
// TODO: remove when all tabs migrated to the new api
|
||||
Util::addScript('files', 'fileinfomodel');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ import PersonalSettings from './components/PersonalSettings.vue'
|
|||
__webpack_nonce__ = getCSPNonce()
|
||||
|
||||
Vue.prototype.t = t
|
||||
|
||||
if (!window.TESTING) {
|
||||
const View = Vue.extend(PersonalSettings)
|
||||
new View().$mount('#files-personal-settings')
|
||||
}
|
||||
const View = Vue.extend(PersonalSettings)
|
||||
const instance = new View()
|
||||
instance.$mount('#files-personal-settings')
|
||||
|
|
|
|||
|
|
@ -1,648 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
describe('OCA.Files.BreadCrumb tests', function() {
|
||||
var BreadCrumb = OCA.Files.BreadCrumb;
|
||||
|
||||
describe('Rendering', function() {
|
||||
var bc;
|
||||
beforeEach(function() {
|
||||
bc = new BreadCrumb({
|
||||
getCrumbUrl: function(part, index) {
|
||||
// for testing purposes
|
||||
return part.dir + '#' + index;
|
||||
}
|
||||
});
|
||||
});
|
||||
afterEach(function() {
|
||||
bc = null;
|
||||
});
|
||||
it('Renders its own container', function() {
|
||||
bc.render();
|
||||
expect(bc.$el.find("ul").hasClass('breadcrumb')).toEqual(true);
|
||||
});
|
||||
it('Renders root by default', function() {
|
||||
var $crumbs;
|
||||
bc.render();
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
// menu and home
|
||||
expect($crumbs.length).toEqual(2);
|
||||
expect($crumbs.eq(0).find('a').hasClass('icon-more')).toEqual(true);
|
||||
expect($crumbs.eq(0).find('div.popovermenu').length).toEqual(1);
|
||||
expect($crumbs.eq(0).data('dir')).not.toBeDefined();
|
||||
expect($crumbs.eq(1).find('a').attr('href')).toEqual('/#1');
|
||||
expect($crumbs.eq(1).find('a').hasClass('icon-home')).toEqual(true);
|
||||
expect($crumbs.eq(1).data('dir')).toEqual('/');
|
||||
});
|
||||
it('Renders root when switching to root', function() {
|
||||
var $crumbs;
|
||||
bc.setDirectory('/somedir');
|
||||
bc.setDirectory('/');
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
expect($crumbs.length).toEqual(2);
|
||||
expect($crumbs.eq(1).data('dir')).toEqual('/');
|
||||
});
|
||||
it('Renders single path section', function() {
|
||||
var $crumbs;
|
||||
bc.setDirectory('/somedir');
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
expect($crumbs.length).toEqual(3);
|
||||
expect($crumbs.eq(0).find('a').hasClass('icon-more')).toEqual(true);
|
||||
expect($crumbs.eq(0).find('div.popovermenu').length).toEqual(1);
|
||||
expect($crumbs.eq(0).data('dir')).not.toBeDefined();
|
||||
|
||||
expect($crumbs.eq(1).find('a').attr('href')).toEqual('/#1');
|
||||
expect($crumbs.eq(1).find('a').hasClass('icon-home')).toEqual(true);
|
||||
expect($crumbs.eq(1).data('dir')).toEqual('/');
|
||||
|
||||
expect($crumbs.eq(2).find('a').attr('href')).toEqual('/somedir#2');
|
||||
expect($crumbs.eq(2).find('img').length).toEqual(0);
|
||||
expect($crumbs.eq(2).data('dir')).toEqual('/somedir');
|
||||
});
|
||||
it('Renders multiple path sections and special chars', function() {
|
||||
var $crumbs;
|
||||
bc.setDirectory('/somedir/with space/abc');
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
expect($crumbs.length).toEqual(5);
|
||||
expect($crumbs.eq(0).find('a').hasClass('icon-more')).toEqual(true);
|
||||
expect($crumbs.eq(0).find('div.popovermenu').length).toEqual(1);
|
||||
expect($crumbs.eq(0).data('dir')).not.toBeDefined();
|
||||
|
||||
expect($crumbs.eq(1).find('a').attr('href')).toEqual('/#1');
|
||||
expect($crumbs.eq(1).find('a').hasClass('icon-home')).toEqual(true);
|
||||
expect($crumbs.eq(1).data('dir')).toEqual('/');
|
||||
|
||||
expect($crumbs.eq(2).find('a').attr('href')).toEqual('/somedir#2');
|
||||
expect($crumbs.eq(2).find('img').length).toEqual(0);
|
||||
expect($crumbs.eq(2).data('dir')).toEqual('/somedir');
|
||||
|
||||
expect($crumbs.eq(3).find('a').attr('href')).toEqual('/somedir/with space#3');
|
||||
expect($crumbs.eq(3).find('img').length).toEqual(0);
|
||||
expect($crumbs.eq(3).data('dir')).toEqual('/somedir/with space');
|
||||
|
||||
expect($crumbs.eq(4).find('a').attr('href')).toEqual('/somedir/with space/abc#4');
|
||||
expect($crumbs.eq(4).find('img').length).toEqual(0);
|
||||
expect($crumbs.eq(4).data('dir')).toEqual('/somedir/with space/abc');
|
||||
});
|
||||
it('Renders backslashes as regular directory separator', function() {
|
||||
var $crumbs;
|
||||
bc.setDirectory('/somedir\\with/mixed\\separators');
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
expect($crumbs.length).toEqual(6);
|
||||
expect($crumbs.eq(0).find('a').hasClass('icon-more')).toEqual(true);
|
||||
expect($crumbs.eq(0).find('div.popovermenu').length).toEqual(1);
|
||||
expect($crumbs.eq(0).data('dir')).not.toBeDefined();
|
||||
|
||||
expect($crumbs.eq(1).find('a').attr('href')).toEqual('/#1');
|
||||
expect($crumbs.eq(1).find('a').hasClass('icon-home')).toEqual(true);
|
||||
expect($crumbs.eq(1).data('dir')).toEqual('/');
|
||||
|
||||
expect($crumbs.eq(2).find('a').attr('href')).toEqual('/somedir#2');
|
||||
expect($crumbs.eq(2).find('img').length).toEqual(0);
|
||||
expect($crumbs.eq(2).data('dir')).toEqual('/somedir');
|
||||
|
||||
expect($crumbs.eq(3).find('a').attr('href')).toEqual('/somedir/with#3');
|
||||
expect($crumbs.eq(3).find('img').length).toEqual(0);
|
||||
expect($crumbs.eq(3).data('dir')).toEqual('/somedir/with');
|
||||
|
||||
expect($crumbs.eq(4).find('a').attr('href')).toEqual('/somedir/with/mixed#4');
|
||||
expect($crumbs.eq(4).find('img').length).toEqual(0);
|
||||
expect($crumbs.eq(4).data('dir')).toEqual('/somedir/with/mixed');
|
||||
|
||||
expect($crumbs.eq(5).find('a').attr('href')).toEqual('/somedir/with/mixed/separators#5');
|
||||
expect($crumbs.eq(5).find('img').length).toEqual(0);
|
||||
expect($crumbs.eq(5).data('dir')).toEqual('/somedir/with/mixed/separators');
|
||||
});
|
||||
});
|
||||
describe('Events', function() {
|
||||
it('Calls onClick handler when clicking on a crumb', function() {
|
||||
var handler = sinon.stub();
|
||||
var bc = new BreadCrumb({
|
||||
onClick: handler
|
||||
});
|
||||
bc.setDirectory('/one/two/three/four');
|
||||
// Click on crumb does not work, only link
|
||||
bc.$el.find('.crumb:eq(4)').click();
|
||||
expect(handler.calledOnce).toEqual(false);
|
||||
|
||||
handler.reset();
|
||||
// Click on crumb link works
|
||||
bc.$el.find('.crumb:eq(1) a').click();
|
||||
expect(handler.calledOnce).toEqual(true);
|
||||
expect(handler.getCall(0).thisValue).toEqual(bc.$el.find('.crumb > a').get(1));
|
||||
});
|
||||
it('Calls onDrop handler when dropping on a crumb', function() {
|
||||
var droppableStub = sinon.stub($.fn, 'droppable');
|
||||
var handler = sinon.stub();
|
||||
var bc = new BreadCrumb({
|
||||
onDrop: handler
|
||||
});
|
||||
bc.setDirectory('/one/two/three/four');
|
||||
expect(droppableStub.calledOnce).toEqual(true);
|
||||
|
||||
expect(droppableStub.getCall(0).args[0].drop).toBeDefined();
|
||||
// simulate drop
|
||||
droppableStub.getCall(0).args[0].drop({dummy: true});
|
||||
|
||||
expect(handler.calledOnce).toEqual(true);
|
||||
expect(handler.getCall(0).args[0]).toEqual({dummy: true});
|
||||
|
||||
droppableStub.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Menu tests', function() {
|
||||
var bc, dummyDir, $crumbmenuLink, $popovermenu;
|
||||
|
||||
beforeEach(function() {
|
||||
dummyDir = '/one/two/three/four/five'
|
||||
|
||||
bc = new BreadCrumb();
|
||||
// append dummy navigation and controls
|
||||
// as they are currently used for measurements
|
||||
$('#testArea').append(
|
||||
'<div class="files-controls"></div>'
|
||||
);
|
||||
$('.files-controls').append(bc.$el);
|
||||
|
||||
bc.setDirectory(dummyDir);
|
||||
|
||||
$('li.crumb').each(function(index){
|
||||
$(this).css('width', 50);
|
||||
$(this).css('padding', 0);
|
||||
$(this).css('margin', 0);
|
||||
});
|
||||
$('div.crumbhome').css('width', 51);
|
||||
$('div.crumbmenu').css('width', 51);
|
||||
|
||||
$('.files-controls').width(1000);
|
||||
bc._resize();
|
||||
|
||||
// Shrink to show popovermenu
|
||||
$('.files-controls').width(300);
|
||||
bc._resize();
|
||||
|
||||
$crumbmenuLink = bc.$el.find('.crumbmenu > a');
|
||||
$popovermenu = $crumbmenuLink.next('.popovermenu');
|
||||
});
|
||||
afterEach(function() {
|
||||
bc = null;
|
||||
});
|
||||
|
||||
it('Shows only items not in the breadcrumb', function() {
|
||||
var hiddenCrumbs = bc.$el.find('.crumb:not(.crumbmenu).hidden');
|
||||
expect($popovermenu.find('li:not(.in-breadcrumb)').length).toEqual(hiddenCrumbs.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Resizing', function() {
|
||||
var bc, dummyDir, widths, paddings, margins;
|
||||
|
||||
// cit() will skip tests if running on PhantomJS because it does not
|
||||
// have proper support for flexboxes.
|
||||
var cit = window.isPhantom?xit:it;
|
||||
|
||||
beforeEach(function() {
|
||||
dummyDir = '/short name/longer name/looooooooooooonger/' +
|
||||
'even longer long long long longer long/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/last one';
|
||||
|
||||
bc = new BreadCrumb();
|
||||
// append dummy navigation and controls
|
||||
// as they are currently used for measurements
|
||||
$('#testArea').append(
|
||||
'<div class="files-controls"></div>'
|
||||
);
|
||||
$('.files-controls').append(bc.$el);
|
||||
|
||||
// triggers resize implicitly
|
||||
bc.setDirectory(dummyDir);
|
||||
|
||||
// using hard-coded widths (pre-measured) to avoid getting different
|
||||
// results on different browsers due to font engine differences
|
||||
// 51px is default size for menu and home
|
||||
widths = [51, 51, 106, 112, 160, 257, 251, 91];
|
||||
// using hard-coded paddings and margins to avoid depending on the
|
||||
// current CSS values used in the server
|
||||
paddings = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
margins = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
$('li.crumb').each(function(index){
|
||||
$(this).css('width', widths[index]);
|
||||
$(this).css('padding', paddings[index]);
|
||||
$(this).css('margin', margins[index]);
|
||||
});
|
||||
});
|
||||
afterEach(function() {
|
||||
bc = null;
|
||||
});
|
||||
it('Hides breadcrumbs to fit available width', function() {
|
||||
var $crumbs;
|
||||
|
||||
$('.files-controls').width(500);
|
||||
bc._resize();
|
||||
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
|
||||
// Second, third, fourth and fifth crumb are hidden and everything
|
||||
// else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
it('Hides breadcrumbs to fit available width', function() {
|
||||
var $crumbs;
|
||||
|
||||
$('.files-controls').width(700);
|
||||
bc._resize();
|
||||
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
|
||||
// Third and fourth crumb are hidden and everything else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
it('Hides breadcrumbs to fit available width taking paddings into account', function() {
|
||||
var $crumbs;
|
||||
|
||||
// Each element is 20px wider
|
||||
paddings = [10, 10, 10, 10, 10, 10, 10, 10];
|
||||
|
||||
$('li.crumb').each(function(index){
|
||||
$(this).css('padding', paddings[index]);
|
||||
});
|
||||
|
||||
$('.files-controls').width(700);
|
||||
bc._resize();
|
||||
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
|
||||
// Second, third and fourth crumb are hidden and everything else is
|
||||
// visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
it('Hides breadcrumbs to fit available width taking margins into account', function() {
|
||||
var $crumbs;
|
||||
|
||||
// Each element is 20px wider
|
||||
margins = [10, 10, 10, 10, 10, 10, 10, 10];
|
||||
|
||||
$('li.crumb').each(function(index){
|
||||
$(this).css('margin', margins[index]);
|
||||
});
|
||||
|
||||
$('.files-controls').width(700);
|
||||
bc._resize();
|
||||
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
|
||||
// Second, third and fourth crumb are hidden and everything else is
|
||||
// visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
it('Hides breadcrumbs to fit available width left by siblings', function() {
|
||||
var $crumbs;
|
||||
|
||||
$('.files-controls').width(700);
|
||||
bc._resize();
|
||||
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
|
||||
// Third and fourth crumb are hidden and everything else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
|
||||
// Visible sibling widths add up to 200px
|
||||
var $previousSibling = $('<div class="otherSibling"></div>');
|
||||
// Set both the width and the min-width to even differences in width
|
||||
// handling in the browsers used to run the tests.
|
||||
$previousSibling.css('width', '50px');
|
||||
$previousSibling.css('min-width', '50px');
|
||||
$('.files-controls').prepend($previousSibling);
|
||||
|
||||
var $creatableActions = $('<div class="actions creatable"></div>');
|
||||
// Set both the width and the min-width to even differences in width
|
||||
// handling in the browsers used to run the tests.
|
||||
$creatableActions.css('width', '100px');
|
||||
$creatableActions.css('min-width', '100px');
|
||||
$('.files-controls').append($creatableActions);
|
||||
|
||||
var $nextHiddenSibling = $('<div class="otherSibling hidden"></div>');
|
||||
// Set both the width and the min-width to even differences in width
|
||||
// handling in the browsers used to run the tests.
|
||||
$nextHiddenSibling.css('width', '200px');
|
||||
$nextHiddenSibling.css('min-width', '200px');
|
||||
$('.files-controls').append($nextHiddenSibling);
|
||||
|
||||
var $nextSibling = $('<div class="otherSibling"></div>');
|
||||
// Set both the width and the min-width to even differences in width
|
||||
// handling in the browsers used to run the tests.
|
||||
$nextSibling.css('width', '50px');
|
||||
$nextSibling.css('min-width', '50px');
|
||||
$('.files-controls').append($nextSibling);
|
||||
|
||||
bc._resize();
|
||||
|
||||
// Second, third, fourth and fifth crumb are hidden and everything
|
||||
// else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
it('Hides breadcrumbs to fit available width left by siblings with paddings and margins', function() {
|
||||
var $crumbs;
|
||||
|
||||
$('.files-controls').width(700);
|
||||
bc._resize();
|
||||
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
|
||||
// Third and fourth crumb are hidden and everything else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
|
||||
// Visible sibling widths add up to 200px
|
||||
var $previousSibling = $('<div class="otherSibling"></div>');
|
||||
// Set both the width and the min-width to even differences in width
|
||||
// handling in the browsers used to run the tests.
|
||||
$previousSibling.css('width', '10px');
|
||||
$previousSibling.css('min-width', '10px');
|
||||
$previousSibling.css('margin', '20px');
|
||||
$('.files-controls').prepend($previousSibling);
|
||||
|
||||
var $creatableActions = $('<div class="actions creatable"></div>');
|
||||
// Set both the width and the min-width to even differences in width
|
||||
// handling in the browsers used to run the tests.
|
||||
$creatableActions.css('width', '20px');
|
||||
$creatableActions.css('min-width', '20px');
|
||||
$creatableActions.css('margin-left', '40px');
|
||||
$creatableActions.css('padding-right', '40px');
|
||||
$('.files-controls').append($creatableActions);
|
||||
|
||||
var $nextHiddenSibling = $('<div class="otherSibling hidden"></div>');
|
||||
// Set both the width and the min-width to even differences in width
|
||||
// handling in the browsers used to run the tests.
|
||||
$nextHiddenSibling.css('width', '200px');
|
||||
$nextHiddenSibling.css('min-width', '200px');
|
||||
$('.files-controls').append($nextHiddenSibling);
|
||||
|
||||
var $nextSibling = $('<div class="otherSibling"></div>');
|
||||
// Set both the width and the min-width to even differences in width
|
||||
// handling in the browsers used to run the tests.
|
||||
$nextSibling.css('width', '10px');
|
||||
$nextSibling.css('min-width', '10px');
|
||||
$nextSibling.css('padding', '20px');
|
||||
$('.files-controls').append($nextSibling);
|
||||
|
||||
bc._resize();
|
||||
|
||||
// Second, third, fourth and fifth crumb are hidden and everything
|
||||
// else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
it('Updates the breadcrumbs when reducing available width', function() {
|
||||
var $crumbs;
|
||||
|
||||
// enough space
|
||||
$('.files-controls').width(1800);
|
||||
bc._resize();
|
||||
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
|
||||
// Menu is hidden
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(true);
|
||||
|
||||
// simulate decrease
|
||||
$('.files-controls').width(950);
|
||||
bc._resize();
|
||||
|
||||
// Third crumb is hidden and everything else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
it('Updates the breadcrumbs when reducing available width taking into account the menu width', function() {
|
||||
var $crumbs;
|
||||
|
||||
// enough space
|
||||
$('.files-controls').width(1800);
|
||||
bc._resize();
|
||||
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
|
||||
// Menu is hidden
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
|
||||
// simulate decrease
|
||||
// 650 is enough for all the crumbs except the third and fourth
|
||||
// ones, but not enough for the menu and all the crumbs except the
|
||||
// third and fourth ones; the second one has to be hidden too.
|
||||
$('.files-controls').width(650);
|
||||
bc._resize();
|
||||
|
||||
// Second, third and fourth crumb are hidden and everything else is
|
||||
// visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
it('Updates the breadcrumbs when increasing available width', function() {
|
||||
var $crumbs;
|
||||
|
||||
// limited space
|
||||
$('.files-controls').width(850);
|
||||
bc._resize();
|
||||
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
|
||||
// Third and fourth crumb are hidden and everything else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
|
||||
// simulate increase
|
||||
$('.files-controls').width(1000);
|
||||
bc._resize();
|
||||
|
||||
// Third crumb is hidden and everything else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
it('Updates the breadcrumbs when increasing available width taking into account the menu width', function() {
|
||||
var $crumbs;
|
||||
|
||||
// limited space
|
||||
$('.files-controls').width(850);
|
||||
bc._resize();
|
||||
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
|
||||
// Third and fourth crumb are hidden and everything else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
|
||||
// simulate increase
|
||||
// 1030 is enough for all the crumbs if the menu is hidden.
|
||||
$('.files-controls').width(1030);
|
||||
bc._resize();
|
||||
|
||||
// Menu is hidden and everything else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
cit('Updates the breadcrumbs when increasing available width with an expanding sibling', function() {
|
||||
var $crumbs;
|
||||
|
||||
// The sibling expands to fill all the width left by the breadcrumbs
|
||||
var $nextSibling = $('<div class="sibling"></div>');
|
||||
// Set both the width and the min-width to even differences in width
|
||||
// handling in the browsers used to run the tests.
|
||||
$nextSibling.css('width', '10px');
|
||||
$nextSibling.css('min-width', '10px');
|
||||
$nextSibling.css('display', 'flex');
|
||||
$nextSibling.css('flex', '1 1');
|
||||
var $nextSiblingChild = $('<div class="siblingChild"></div>');
|
||||
$nextSiblingChild.css('margin-left', 'auto');
|
||||
$nextSibling.append($nextSiblingChild);
|
||||
$('.files-controls').append($nextSibling);
|
||||
|
||||
// limited space
|
||||
$('.files-controls').width(850);
|
||||
bc._resize();
|
||||
|
||||
$crumbs = bc.$el.find('.crumb');
|
||||
|
||||
// Third and fourth crumb are hidden and everything else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
|
||||
// simulate increase
|
||||
$('.files-controls').width(1000);
|
||||
bc._resize();
|
||||
|
||||
// Third crumb is hidden and everything else is visible
|
||||
expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
|
||||
|
||||
expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
|
||||
expect($crumbs.eq(5).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
|
||||
expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,240 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
describe('OCA.Files.DetailsView tests', function() {
|
||||
var detailsView;
|
||||
|
||||
beforeEach(function() {
|
||||
detailsView = new OCA.Files.DetailsView();
|
||||
});
|
||||
afterEach(function() {
|
||||
detailsView.remove();
|
||||
detailsView = undefined;
|
||||
});
|
||||
it('renders itself empty when nothing registered', function() {
|
||||
detailsView.render();
|
||||
expect(detailsView.$el.find('.detailFileInfoContainer').length).toEqual(1);
|
||||
expect(detailsView.$el.find('.tabsContainer').length).toEqual(1);
|
||||
});
|
||||
describe('file info detail view', function() {
|
||||
it('returns registered view', function() {
|
||||
var testView = new OCA.Files.DetailFileInfoView();
|
||||
var testView2 = new OCA.Files.DetailFileInfoView();
|
||||
detailsView.addDetailView(testView);
|
||||
detailsView.addDetailView(testView2);
|
||||
|
||||
detailViews = detailsView.getDetailViews();
|
||||
|
||||
expect(detailViews).toContain(testView);
|
||||
expect(detailViews).toContain(testView2);
|
||||
|
||||
// Modify array and check that registered detail views are not
|
||||
// modified
|
||||
detailViews.pop();
|
||||
detailViews.pop();
|
||||
|
||||
detailViews = detailsView.getDetailViews();
|
||||
|
||||
expect(detailViews).toContain(testView);
|
||||
expect(detailViews).toContain(testView2);
|
||||
});
|
||||
it('renders registered view', function() {
|
||||
var testView = new OCA.Files.DetailFileInfoView();
|
||||
var testView2 = new OCA.Files.DetailFileInfoView();
|
||||
detailsView.addDetailView(testView);
|
||||
detailsView.addDetailView(testView2);
|
||||
detailsView.render();
|
||||
|
||||
expect(detailsView.$el.find('.detailFileInfoContainer .detailFileInfoView').length).toEqual(2);
|
||||
});
|
||||
it('updates registered tabs when fileinfo is updated', function() {
|
||||
var viewRenderStub = sinon.stub(OCA.Files.DetailFileInfoView.prototype, 'render');
|
||||
var testView = new OCA.Files.DetailFileInfoView();
|
||||
var testView2 = new OCA.Files.DetailFileInfoView();
|
||||
detailsView.addDetailView(testView);
|
||||
detailsView.addDetailView(testView2);
|
||||
detailsView.render();
|
||||
|
||||
var fileInfo = {id: 5, name: 'test.txt'};
|
||||
viewRenderStub.reset();
|
||||
detailsView.setFileInfo(fileInfo);
|
||||
|
||||
expect(testView.getFileInfo()).toEqual(fileInfo);
|
||||
expect(testView2.getFileInfo()).toEqual(fileInfo);
|
||||
|
||||
expect(viewRenderStub.callCount).toEqual(2);
|
||||
viewRenderStub.restore();
|
||||
});
|
||||
});
|
||||
describe('tabs', function() {
|
||||
var testView, testView2;
|
||||
|
||||
beforeEach(function() {
|
||||
testView = new OCA.Files.DetailTabView({id: 'test1'});
|
||||
testView2 = new OCA.Files.DetailTabView({id: 'test2'});
|
||||
detailsView.addTabView(testView);
|
||||
detailsView.addTabView(testView2);
|
||||
detailsView.render();
|
||||
});
|
||||
it('initially renders only the selected tab', function() {
|
||||
expect(detailsView.$el.find('.tab').length).toEqual(1);
|
||||
expect(detailsView.$el.find('.tab').attr('id')).toEqual('test1');
|
||||
});
|
||||
it('updates tab model and rerenders on-demand as soon as it gets selected', function() {
|
||||
var tab1RenderStub = sinon.stub(testView, 'render');
|
||||
var tab2RenderStub = sinon.stub(testView2, 'render');
|
||||
var fileInfo1 = new OCA.Files.FileInfoModel({id: 5, name: 'test.txt'});
|
||||
var fileInfo2 = new OCA.Files.FileInfoModel({id: 8, name: 'test2.txt'});
|
||||
|
||||
detailsView.setFileInfo(fileInfo1);
|
||||
|
||||
// first tab renders, not the second one
|
||||
expect(tab1RenderStub.calledOnce).toEqual(true);
|
||||
expect(tab2RenderStub.notCalled).toEqual(true);
|
||||
|
||||
// info got set only to the first visible tab
|
||||
expect(testView.getFileInfo()).toEqual(fileInfo1);
|
||||
expect(testView2.getFileInfo()).toBeUndefined();
|
||||
|
||||
// select second tab for first render
|
||||
detailsView.$el.find('.tabHeader').eq(1).click();
|
||||
|
||||
// second tab got rendered
|
||||
expect(tab2RenderStub.calledOnce).toEqual(true);
|
||||
expect(testView2.getFileInfo()).toEqual(fileInfo1);
|
||||
|
||||
// select the first tab again
|
||||
detailsView.$el.find('.tabHeader').eq(0).click();
|
||||
|
||||
// no re-render
|
||||
expect(tab1RenderStub.calledOnce).toEqual(true);
|
||||
expect(tab2RenderStub.calledOnce).toEqual(true);
|
||||
|
||||
tab1RenderStub.reset();
|
||||
tab2RenderStub.reset();
|
||||
|
||||
// switch to another file
|
||||
detailsView.setFileInfo(fileInfo2);
|
||||
|
||||
// only the visible tab was updated and rerendered
|
||||
expect(tab1RenderStub.calledOnce).toEqual(true);
|
||||
expect(testView.getFileInfo()).toEqual(fileInfo2);
|
||||
|
||||
// second/invisible tab still has old info, not rerendered
|
||||
expect(tab2RenderStub.notCalled).toEqual(true);
|
||||
expect(testView2.getFileInfo()).toEqual(fileInfo1);
|
||||
|
||||
// reselect the second one
|
||||
detailsView.$el.find('.tabHeader').eq(1).click();
|
||||
|
||||
// second tab becomes visible, updated and rendered
|
||||
expect(testView2.getFileInfo()).toEqual(fileInfo2);
|
||||
expect(tab2RenderStub.calledOnce).toEqual(true);
|
||||
|
||||
tab1RenderStub.restore();
|
||||
tab2RenderStub.restore();
|
||||
});
|
||||
it('selects the first tab by default', function() {
|
||||
expect(detailsView.$el.find('.tabHeader').eq(0).hasClass('selected')).toEqual(true);
|
||||
expect(detailsView.$el.find('.tabHeader').eq(1).hasClass('selected')).toEqual(false);
|
||||
expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(false);
|
||||
expect(detailsView.$el.find('.tab').eq(1).length).toEqual(0);
|
||||
});
|
||||
it('switches the current tab when clicking on tab header', function() {
|
||||
detailsView.$el.find('.tabHeader').eq(1).click();
|
||||
expect(detailsView.$el.find('.tabHeader').eq(0).hasClass('selected')).toEqual(false);
|
||||
expect(detailsView.$el.find('.tabHeader').eq(1).hasClass('selected')).toEqual(true);
|
||||
expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(true);
|
||||
expect(detailsView.$el.find('.tab').eq(1).hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
describe('tab visibility', function() {
|
||||
beforeEach(function() {
|
||||
detailsView.remove();
|
||||
detailsView = new OCA.Files.DetailsView();
|
||||
});
|
||||
it('does not display tab headers when only one tab exists', function() {
|
||||
testView = new OCA.Files.DetailTabView({id: 'test1'});
|
||||
detailsView.addTabView(testView);
|
||||
detailsView.render();
|
||||
|
||||
expect(detailsView.$el.find('.tabHeaders').hasClass('hidden')).toEqual(true);
|
||||
expect(detailsView.$el.find('.tabHeader').length).toEqual(1);
|
||||
});
|
||||
it('does not display tab that do not pass visibility check', function() {
|
||||
testView = new OCA.Files.DetailTabView({id: 'test1'});
|
||||
testView.canDisplay = sinon.stub().returns(false);
|
||||
testView2 = new OCA.Files.DetailTabView({id: 'test2'});
|
||||
var testView3 = new OCA.Files.DetailTabView({id: 'test3'});
|
||||
detailsView.addTabView(testView);
|
||||
detailsView.addTabView(testView2);
|
||||
detailsView.addTabView(testView3);
|
||||
|
||||
var fileInfo = {id: 5, name: 'test.txt'};
|
||||
detailsView.setFileInfo(fileInfo);
|
||||
|
||||
expect(testView.canDisplay.calledOnce).toEqual(true);
|
||||
expect(testView.canDisplay.calledWith(fileInfo)).toEqual(true);
|
||||
|
||||
expect(detailsView.$el.find('.tabHeaders').hasClass('hidden')).toEqual(false);
|
||||
expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('hidden')).toEqual(true);
|
||||
expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('hidden')).toEqual(false);
|
||||
expect(detailsView.$el.find('.tabHeader[data-tabid=test3]').hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
it('does not show tab headers if only one header is visible due to visibility check', function() {
|
||||
testView = new OCA.Files.DetailTabView({id: 'test1'});
|
||||
testView.canDisplay = sinon.stub().returns(false);
|
||||
testView2 = new OCA.Files.DetailTabView({id: 'test2'});
|
||||
detailsView.addTabView(testView);
|
||||
detailsView.addTabView(testView2);
|
||||
|
||||
var fileInfo = {id: 5, name: 'test.txt'};
|
||||
detailsView.setFileInfo(fileInfo);
|
||||
|
||||
expect(testView.canDisplay.calledOnce).toEqual(true);
|
||||
expect(testView.canDisplay.calledWith(fileInfo)).toEqual(true);
|
||||
|
||||
expect(detailsView.$el.find('.tabHeaders').hasClass('hidden')).toEqual(true);
|
||||
expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('hidden')).toEqual(true);
|
||||
expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
it('deselects the current tab if a model update does not pass the visibility check', function() {
|
||||
testView = new OCA.Files.DetailTabView({id: 'test1'});
|
||||
testView.canDisplay = function(fileInfo) {
|
||||
return fileInfo.mimetype !== 'httpd/unix-directory';
|
||||
};
|
||||
testView2 = new OCA.Files.DetailTabView({id: 'test2'});
|
||||
detailsView.addTabView(testView);
|
||||
detailsView.addTabView(testView2);
|
||||
|
||||
var fileInfo = {id: 5, name: 'test.txt', mimetype: 'text/plain'};
|
||||
detailsView.setFileInfo(fileInfo);
|
||||
|
||||
expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('selected')).toEqual(true);
|
||||
expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('selected')).toEqual(false);
|
||||
|
||||
detailsView.setFileInfo({id: 10, name: 'folder', mimetype: 'httpd/unix-directory'});
|
||||
|
||||
expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('selected')).toEqual(false);
|
||||
expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('selected')).toEqual(true);
|
||||
});
|
||||
});
|
||||
it('sorts by order and then label', function() {
|
||||
detailsView.remove();
|
||||
detailsView = new OCA.Files.DetailsView();
|
||||
detailsView.addTabView(new OCA.Files.DetailTabView({id: 'abc', order: 20}));
|
||||
detailsView.addTabView(new OCA.Files.DetailTabView({id: 'def', order: 10}));
|
||||
detailsView.addTabView(new OCA.Files.DetailTabView({id: 'jkl'}));
|
||||
detailsView.addTabView(new OCA.Files.DetailTabView({id: 'ghi'}));
|
||||
detailsView.render();
|
||||
|
||||
var tabs = detailsView.$el.find('.tabHeader').map(function() {
|
||||
return $(this).attr('data-tabid');
|
||||
}).toArray();
|
||||
|
||||
expect(tabs).toEqual(['ghi', 'jkl', 'def', 'abc']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,739 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
describe('OCA.Files.FileActions tests', function() {
|
||||
var fileList, fileActions, clock;
|
||||
|
||||
beforeEach(function() {
|
||||
clock = sinon.useFakeTimers();
|
||||
// init horrible parameters
|
||||
var $body = $('#testArea');
|
||||
$body.append('<input type="hidden" id="permissions" value="31"></input>');
|
||||
$body.append('<table class="files-filestable list-container view-grid"><tbody class="files-fileList"></tbody></table>');
|
||||
// dummy files table
|
||||
fileActions = new OCA.Files.FileActions();
|
||||
fileActions.registerAction({
|
||||
name: 'Testdropdown',
|
||||
displayName: 'Testdropdowndisplay',
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: function () {
|
||||
return OC.imagePath('core', 'actions/download');
|
||||
}
|
||||
});
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'Testinline',
|
||||
displayName: 'Testinlinedisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'Testdefault',
|
||||
displayName: 'Testdefaultdisplay',
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
fileActions.setDefault('all', 'Testdefault');
|
||||
fileList = new OCA.Files.FileList($body, {
|
||||
fileActions: fileActions
|
||||
});
|
||||
fileList.changeDirectory('/subdir', false, true);
|
||||
});
|
||||
afterEach(function() {
|
||||
fileActions = null;
|
||||
fileList.destroy();
|
||||
fileList = undefined;
|
||||
clock.restore();
|
||||
$('#permissions, .files-filestable').remove();
|
||||
});
|
||||
it('calling clear() clears file actions', function() {
|
||||
fileActions.clear();
|
||||
expect(fileActions.actions).toEqual({});
|
||||
expect(fileActions.defaults).toEqual({});
|
||||
expect(fileActions.icons).toEqual({});
|
||||
expect(fileActions.currentFile).toBe(null);
|
||||
});
|
||||
describe('displaying actions', function() {
|
||||
var $tr;
|
||||
|
||||
beforeEach(function() {
|
||||
var fileData = {
|
||||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456',
|
||||
permissions: OC.PERMISSION_READ | OC.PERMISSION_UPDATE
|
||||
};
|
||||
|
||||
// note: FileActions.display() is called implicitly
|
||||
$tr = fileList.add(fileData);
|
||||
});
|
||||
it('renders inline file actions', function() {
|
||||
// actions defined after call
|
||||
expect($tr.find('.action.action-testinline').length).toEqual(1);
|
||||
expect($tr.find('.action.action-testinline').attr('data-action')).toEqual('Testinline');
|
||||
});
|
||||
it('does not render dropdown actions', function() {
|
||||
expect($tr.find('.action.action-testdropdown').length).toEqual(0);
|
||||
});
|
||||
it('does not render default action', function() {
|
||||
expect($tr.find('.action.action-testdefault').length).toEqual(0);
|
||||
});
|
||||
it('replaces file actions when displayed twice', function() {
|
||||
fileActions.display($tr.find('td.filename'), true, fileList);
|
||||
fileActions.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action.action-testinline').length).toEqual(1);
|
||||
});
|
||||
it('renders actions menu trigger', function() {
|
||||
expect($tr.find('.action.action-menu').length).toEqual(1);
|
||||
expect($tr.find('.action.action-menu').attr('data-action')).toEqual('menu');
|
||||
});
|
||||
it('only renders actions relevant to the mime type', function() {
|
||||
fileActions.registerAction({
|
||||
name: 'Match',
|
||||
displayName: 'MatchDisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
fileActions.registerAction({
|
||||
name: 'Nomatch',
|
||||
displayName: 'NoMatchDisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'application/octet-stream',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
|
||||
fileActions.display($tr.find('td.filename'), true, fileList);
|
||||
expect($tr.find('.action.action-match').length).toEqual(1);
|
||||
expect($tr.find('.action.action-nomatch').length).toEqual(0);
|
||||
});
|
||||
it('only renders actions relevant to the permissions', function() {
|
||||
fileActions.registerAction({
|
||||
name: 'Match',
|
||||
displayName: 'MatchDisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_UPDATE
|
||||
});
|
||||
fileActions.registerAction({
|
||||
name: 'Nomatch',
|
||||
displayName: 'NoMatchDisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_DELETE
|
||||
});
|
||||
|
||||
fileActions.display($tr.find('td.filename'), true, fileList);
|
||||
expect($tr.find('.action.action-match').length).toEqual(1);
|
||||
expect($tr.find('.action.action-nomatch').length).toEqual(0);
|
||||
});
|
||||
it('display inline icon with image path', function() {
|
||||
fileActions.registerAction({
|
||||
name: 'Icon',
|
||||
displayName: 'IconDisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
icon: OC.imagePath('core', 'actions/icon'),
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
fileActions.registerAction({
|
||||
name: 'NoIcon',
|
||||
displayName: 'NoIconDisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
|
||||
fileActions.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action.action-icon').length).toEqual(1);
|
||||
expect($tr.find('.action.action-icon').find('img').length).toEqual(1);
|
||||
expect($tr.find('.action.action-icon').find('img').eq(0).attr('src')).toEqual(OC.imagePath('core', 'actions/icon'));
|
||||
|
||||
expect($tr.find('.action.action-noicon').length).toEqual(1);
|
||||
expect($tr.find('.action.action-noicon').find('img').length).toEqual(0);
|
||||
});
|
||||
it('display alt text on inline icon with image path', function() {
|
||||
fileActions.registerAction({
|
||||
name: 'IconAltText',
|
||||
displayName: 'IconAltTextDisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
icon: OC.imagePath('core', 'actions/iconAltText'),
|
||||
altText: 'alt icon text',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'IconNoAltText',
|
||||
displayName: 'IconNoAltTextDisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
icon: OC.imagePath('core', 'actions/iconNoAltText'),
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
|
||||
fileActions.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action.action-iconalttext').length).toEqual(1);
|
||||
expect($tr.find('.action.action-iconalttext').find('img').length).toEqual(1);
|
||||
expect($tr.find('.action.action-iconalttext').find('img').eq(0).attr('alt')).toEqual('alt icon text');
|
||||
|
||||
expect($tr.find('.action.action-iconnoalttext').length).toEqual(1);
|
||||
expect($tr.find('.action.action-iconnoalttext').find('img').length).toEqual(1);
|
||||
expect($tr.find('.action.action-iconnoalttext').find('img').eq(0).attr('alt')).toEqual('');
|
||||
});
|
||||
it('display inline icon with iconClass', function() {
|
||||
fileActions.registerAction({
|
||||
name: 'Icon',
|
||||
displayName: 'IconDisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
iconClass: 'icon-test',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
fileActions.registerAction({
|
||||
name: 'NoIcon',
|
||||
displayName: 'NoIconDisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
|
||||
fileActions.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action.action-icon').length).toEqual(1);
|
||||
expect($tr.find('.action.action-icon').find('.icon').length).toEqual(1);
|
||||
expect($tr.find('.action.action-icon').find('.icon').hasClass('icon-test')).toEqual(true);
|
||||
|
||||
expect($tr.find('.action.action-noicon').length).toEqual(1);
|
||||
expect($tr.find('.action.action-noicon').find('.icon').length).toEqual(0);
|
||||
});
|
||||
it('display alt text on inline icon with iconClass when no display name exists', function() {
|
||||
fileActions.registerAction({
|
||||
name: 'IconAltText',
|
||||
displayName: '',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
iconClass: 'icon-alttext',
|
||||
altText: 'alt icon text',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'IconNoAltText',
|
||||
displayName: 'IconNoAltTextDisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
altText: 'useless alt text',
|
||||
iconClass: 'icon-noalttext',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
|
||||
fileActions.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action.action-iconalttext').length).toEqual(1);
|
||||
expect($tr.find('.action.action-iconalttext').find('.icon').length).toEqual(1);
|
||||
expect($tr.find('.action.action-iconalttext').find('.hidden-visually').text()).toEqual('alt icon text');
|
||||
|
||||
expect($tr.find('.action.action-iconnoalttext').length).toEqual(1);
|
||||
expect($tr.find('.action.action-iconnoalttext').find('.icon').length).toEqual(1);
|
||||
expect($tr.find('.action.action-iconnoalttext').find('.hidden-visually').length).toEqual(0);
|
||||
});
|
||||
});
|
||||
describe('action handler', function() {
|
||||
var actionStub, $tr, clock;
|
||||
|
||||
beforeEach(function() {
|
||||
clock = sinon.useFakeTimers();
|
||||
var fileData = {
|
||||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456'
|
||||
};
|
||||
actionStub = sinon.stub();
|
||||
fileActions.registerAction({
|
||||
name: 'Test',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
permissions: OC.PERMISSION_READ,
|
||||
actionHandler: actionStub
|
||||
});
|
||||
$tr = fileList.add(fileData);
|
||||
});
|
||||
afterEach(function() {
|
||||
OC.hideMenus();
|
||||
// jump past animations
|
||||
clock.tick(1000);
|
||||
clock.restore();
|
||||
});
|
||||
it('passes context to action handler', function() {
|
||||
var notifyUpdateListenersSpy = sinon.spy(fileList.fileActions, '_notifyUpdateListeners');
|
||||
expect($tr.length).toEqual(1);
|
||||
expect($tr.find('.action-test').length).toEqual(1);
|
||||
$tr.find('.action-test').click();
|
||||
expect(actionStub.calledOnce).toEqual(true);
|
||||
expect(actionStub.getCall(0).args[0]).toEqual('testName.txt');
|
||||
var context = actionStub.getCall(0).args[1];
|
||||
expect(context.$file.is($tr)).toEqual(true);
|
||||
expect(context.fileList).toBeDefined();
|
||||
expect(context.fileActions).toBeDefined();
|
||||
expect(context.dir).toEqual('/subdir');
|
||||
expect(context.fileInfoModel.get('name')).toEqual('testName.txt');
|
||||
|
||||
expect(notifyUpdateListenersSpy.calledTwice).toEqual(true);
|
||||
expect(notifyUpdateListenersSpy.calledBefore(actionStub)).toEqual(true);
|
||||
expect(notifyUpdateListenersSpy.calledAfter(actionStub)).toEqual(true);
|
||||
expect(notifyUpdateListenersSpy.getCall(0).args[0]).toEqual('beforeTriggerAction');
|
||||
expect(notifyUpdateListenersSpy.getCall(0).args[1]).toEqual({
|
||||
action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
|
||||
fileName: 'testName.txt',
|
||||
context: context
|
||||
});
|
||||
expect(notifyUpdateListenersSpy.getCall(1).args[0]).toEqual('afterTriggerAction');
|
||||
expect(notifyUpdateListenersSpy.getCall(1).args[1]).toEqual({
|
||||
action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
|
||||
fileName: 'testName.txt',
|
||||
context: context
|
||||
});
|
||||
|
||||
// when data-path is defined
|
||||
actionStub.reset();
|
||||
$tr.attr('data-path', '/somepath');
|
||||
$tr.find('.action-test').click();
|
||||
context = actionStub.getCall(0).args[1];
|
||||
expect(context.dir).toEqual('/somepath');
|
||||
});
|
||||
it('also triggers action handler when calling triggerAction()', function() {
|
||||
var notifyUpdateListenersSpy = sinon.spy(fileList.fileActions, '_notifyUpdateListeners');
|
||||
var model = new OCA.Files.FileInfoModel({
|
||||
id: 1,
|
||||
name: 'Test.txt',
|
||||
path: '/subdir',
|
||||
mime: 'text/plain',
|
||||
permissions: 31
|
||||
});
|
||||
fileActions.triggerAction('Test', model, fileList);
|
||||
|
||||
expect(actionStub.calledOnce).toEqual(true);
|
||||
expect(actionStub.getCall(0).args[0]).toEqual('Test.txt');
|
||||
expect(actionStub.getCall(0).args[1].fileList).toEqual(fileList);
|
||||
expect(actionStub.getCall(0).args[1].fileActions).toEqual(fileActions);
|
||||
expect(actionStub.getCall(0).args[1].fileInfoModel).toEqual(model);
|
||||
|
||||
expect(notifyUpdateListenersSpy.calledTwice).toEqual(true);
|
||||
expect(notifyUpdateListenersSpy.calledBefore(actionStub)).toEqual(true);
|
||||
expect(notifyUpdateListenersSpy.calledAfter(actionStub)).toEqual(true);
|
||||
expect(notifyUpdateListenersSpy.getCall(0).args[0]).toEqual('beforeTriggerAction');
|
||||
expect(notifyUpdateListenersSpy.getCall(0).args[1]).toEqual({
|
||||
action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
|
||||
fileName: 'Test.txt',
|
||||
context: {
|
||||
fileActions: fileActions,
|
||||
fileInfoModel: model,
|
||||
dir: '/subdir',
|
||||
fileList: fileList,
|
||||
$file: fileList.findFileEl('Test.txt')
|
||||
}
|
||||
});
|
||||
expect(notifyUpdateListenersSpy.getCall(1).args[0]).toEqual('afterTriggerAction');
|
||||
expect(notifyUpdateListenersSpy.getCall(1).args[1]).toEqual({
|
||||
action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
|
||||
fileName: 'Test.txt',
|
||||
context: {
|
||||
fileActions: fileActions,
|
||||
fileInfoModel: model,
|
||||
dir: '/subdir',
|
||||
fileList: fileList,
|
||||
$file: fileList.findFileEl('Test.txt')
|
||||
}
|
||||
});
|
||||
});
|
||||
it('triggers listener events when invoked directly', function() {
|
||||
var context = {fileActions: new OCA.Files.FileActions()}
|
||||
var notifyUpdateListenersSpy = sinon.spy(context.fileActions, '_notifyUpdateListeners');
|
||||
var testAction = fileActions.get('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'];
|
||||
|
||||
testAction('Test.txt', context);
|
||||
|
||||
expect(actionStub.calledOnce).toEqual(true);
|
||||
expect(actionStub.getCall(0).args[0]).toEqual('Test.txt');
|
||||
expect(actionStub.getCall(0).args[1]).toBe(context);
|
||||
|
||||
expect(notifyUpdateListenersSpy.calledTwice).toEqual(true);
|
||||
expect(notifyUpdateListenersSpy.calledBefore(actionStub)).toEqual(true);
|
||||
expect(notifyUpdateListenersSpy.calledAfter(actionStub)).toEqual(true);
|
||||
expect(notifyUpdateListenersSpy.getCall(0).args[0]).toEqual('beforeTriggerAction');
|
||||
expect(notifyUpdateListenersSpy.getCall(0).args[1]).toEqual({
|
||||
action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
|
||||
fileName: 'Test.txt',
|
||||
context: context
|
||||
});
|
||||
expect(notifyUpdateListenersSpy.getCall(1).args[0]).toEqual('afterTriggerAction');
|
||||
expect(notifyUpdateListenersSpy.getCall(1).args[1]).toEqual({
|
||||
action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
|
||||
fileName: 'Test.txt',
|
||||
context: context
|
||||
});
|
||||
}),
|
||||
describe('actions menu', function() {
|
||||
it('shows actions menu inside row when clicking the menu trigger', function() {
|
||||
expect($tr.find('td.filename .fileActionsMenu').length).toEqual(0);
|
||||
$tr.find('.action-menu').click();
|
||||
expect($tr.find('td.filename .fileActionsMenu').length).toEqual(1);
|
||||
});
|
||||
it('shows highlight on current row', function() {
|
||||
$tr.find('.action-menu').click();
|
||||
expect($tr.hasClass('mouseOver')).toEqual(true);
|
||||
});
|
||||
it('cleans up after hiding', function() {
|
||||
var slideUpStub = sinon.stub($.fn, 'slideUp');
|
||||
$tr.find('.action-menu').click();
|
||||
expect($tr.find('.fileActionsMenu').length).toEqual(1);
|
||||
OC.hideMenus();
|
||||
// sliding animation
|
||||
expect(slideUpStub.calledOnce).toEqual(true);
|
||||
slideUpStub.getCall(0).args[1]();
|
||||
expect($tr.hasClass('mouseOver')).toEqual(false);
|
||||
expect($tr.find('.fileActionsMenu').length).toEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('custom rendering', function() {
|
||||
var $tr;
|
||||
beforeEach(function() {
|
||||
var fileData = {
|
||||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456'
|
||||
};
|
||||
$tr = fileList.add(fileData);
|
||||
});
|
||||
it('regular function', function() {
|
||||
var actionStub = sinon.stub();
|
||||
fileActions.registerAction({
|
||||
name: 'Test',
|
||||
displayName: '',
|
||||
mime: 'all',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
permissions: OC.PERMISSION_READ,
|
||||
render: function(actionSpec, isDefault, context) {
|
||||
expect(actionSpec.name).toEqual('Test');
|
||||
expect(actionSpec.displayName).toEqual('');
|
||||
expect(actionSpec.permissions).toEqual(OC.PERMISSION_READ);
|
||||
expect(actionSpec.mime).toEqual('all');
|
||||
expect(isDefault).toEqual(false);
|
||||
|
||||
expect(context.fileList).toEqual(fileList);
|
||||
expect(context.$file[0]).toEqual($tr[0]);
|
||||
|
||||
var $customEl = $('<a class="action action-test" href="#"><span>blabli</span><span>blabla</span></a>');
|
||||
$tr.find('td:first').append($customEl);
|
||||
return $customEl;
|
||||
},
|
||||
actionHandler: actionStub
|
||||
});
|
||||
fileActions.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
var $actionEl = $tr.find('td:first .action-test');
|
||||
expect($actionEl.length).toEqual(1);
|
||||
expect($actionEl.hasClass('action')).toEqual(true);
|
||||
|
||||
$actionEl.click();
|
||||
expect(actionStub.calledOnce).toEqual(true);
|
||||
expect(actionStub.getCall(0).args[0]).toEqual('testName.txt');
|
||||
});
|
||||
});
|
||||
describe('merging', function() {
|
||||
var $tr;
|
||||
beforeEach(function() {
|
||||
var fileData = {
|
||||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
path: '/anotherpath/there',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456'
|
||||
};
|
||||
$tr = fileList.add(fileData);
|
||||
});
|
||||
afterEach(function() {
|
||||
$tr = null;
|
||||
});
|
||||
it('copies all actions to target file actions', function() {
|
||||
var actions1 = new OCA.Files.FileActions();
|
||||
var actions2 = new OCA.Files.FileActions();
|
||||
var actionStub1 = sinon.stub();
|
||||
var actionStub2 = sinon.stub();
|
||||
actions1.registerAction({
|
||||
name: 'Test',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
actionHandler: actionStub1
|
||||
});
|
||||
actions2.registerAction({
|
||||
name: 'Test2',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
actionHandler: actionStub2
|
||||
});
|
||||
actions2.merge(actions1);
|
||||
|
||||
actions2.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action-test').length).toEqual(1);
|
||||
expect($tr.find('.action-test2').length).toEqual(1);
|
||||
|
||||
$tr.find('.action-test').click();
|
||||
expect(actionStub1.calledOnce).toEqual(true);
|
||||
expect(actionStub2.notCalled).toEqual(true);
|
||||
|
||||
actionStub1.reset();
|
||||
|
||||
$tr.find('.action-test2').click();
|
||||
expect(actionStub1.notCalled).toEqual(true);
|
||||
expect(actionStub2.calledOnce).toEqual(true);
|
||||
});
|
||||
it('overrides existing actions on merge', function() {
|
||||
var actions1 = new OCA.Files.FileActions();
|
||||
var actions2 = new OCA.Files.FileActions();
|
||||
var actionStub1 = sinon.stub();
|
||||
var actionStub2 = sinon.stub();
|
||||
actions1.registerAction({
|
||||
name: 'Test',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
actionHandler: actionStub1
|
||||
});
|
||||
actions2.registerAction({
|
||||
name: 'Test', // override
|
||||
mime: 'all',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
actionHandler: actionStub2
|
||||
});
|
||||
actions1.merge(actions2);
|
||||
|
||||
actions1.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action-test').length).toEqual(1);
|
||||
|
||||
$tr.find('.action-test').click();
|
||||
expect(actionStub1.notCalled).toEqual(true);
|
||||
expect(actionStub2.calledOnce).toEqual(true);
|
||||
});
|
||||
it('overrides existing action when calling register after merge', function() {
|
||||
var actions1 = new OCA.Files.FileActions();
|
||||
var actions2 = new OCA.Files.FileActions();
|
||||
var actionStub1 = sinon.stub();
|
||||
var actionStub2 = sinon.stub();
|
||||
actions1.registerAction({
|
||||
mime: 'all',
|
||||
name: 'Test',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
actionHandler: actionStub1
|
||||
});
|
||||
|
||||
actions1.merge(actions2);
|
||||
|
||||
// late override
|
||||
actions1.registerAction({
|
||||
mime: 'all',
|
||||
name: 'Test', // override
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
actionHandler: actionStub2
|
||||
});
|
||||
|
||||
actions1.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action-test').length).toEqual(1);
|
||||
|
||||
$tr.find('.action-test').click();
|
||||
expect(actionStub1.notCalled).toEqual(true);
|
||||
expect(actionStub2.calledOnce).toEqual(true);
|
||||
});
|
||||
it('leaves original file actions untouched (clean copy)', function() {
|
||||
var actions1 = new OCA.Files.FileActions();
|
||||
var actions2 = new OCA.Files.FileActions();
|
||||
var actionStub1 = sinon.stub();
|
||||
var actionStub2 = sinon.stub();
|
||||
actions1.registerAction({
|
||||
mime: 'all',
|
||||
name: 'Test',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
actionHandler: actionStub1
|
||||
});
|
||||
|
||||
// copy the Test action to actions2
|
||||
actions2.merge(actions1);
|
||||
|
||||
// late override
|
||||
actions2.registerAction({
|
||||
mime: 'all',
|
||||
name: 'Test', // override
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
actionHandler: actionStub2
|
||||
});
|
||||
|
||||
// check if original actions still call the correct handler
|
||||
actions1.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
expect($tr.find('.action-test').length).toEqual(1);
|
||||
|
||||
$tr.find('.action-test').click();
|
||||
expect(actionStub1.calledOnce).toEqual(true);
|
||||
expect(actionStub2.notCalled).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe('events', function() {
|
||||
var clock;
|
||||
beforeEach(function() {
|
||||
clock = sinon.useFakeTimers();
|
||||
});
|
||||
afterEach(function() {
|
||||
clock.restore();
|
||||
});
|
||||
it('notifies update event handlers once after multiple changes', function() {
|
||||
var actionStub = sinon.stub();
|
||||
var handler = sinon.stub();
|
||||
fileActions.on('registerAction', handler);
|
||||
fileActions.registerAction({
|
||||
mime: 'all',
|
||||
name: 'Test',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
actionHandler: actionStub
|
||||
});
|
||||
fileActions.registerAction({
|
||||
mime: 'all',
|
||||
name: 'Test2',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
actionHandler: actionStub
|
||||
});
|
||||
expect(handler.calledTwice).toEqual(true);
|
||||
});
|
||||
it('does not notifies update event handlers after unregistering', function() {
|
||||
var actionStub = sinon.stub();
|
||||
var handler = sinon.stub();
|
||||
fileActions.on('registerAction', handler);
|
||||
fileActions.off('registerAction', handler);
|
||||
fileActions.registerAction({
|
||||
mime: 'all',
|
||||
name: 'Test',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
actionHandler: actionStub
|
||||
});
|
||||
fileActions.registerAction({
|
||||
mime: 'all',
|
||||
name: 'Test2',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: OC.imagePath('core', 'actions/test'),
|
||||
actionHandler: actionStub
|
||||
});
|
||||
expect(handler.notCalled).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe('default actions', function() {
|
||||
describe('download', function() {
|
||||
it('redirects to URL and sets busy state to list', function() {
|
||||
var handleDownloadStub = sinon.stub(OCA.Files.Files, 'handleDownload');
|
||||
var busyStub = sinon.stub(fileList, 'showFileBusyState');
|
||||
var fileData = {
|
||||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456',
|
||||
permissions: OC.PERMISSION_READ | OC.PERMISSION_UPDATE
|
||||
};
|
||||
|
||||
// note: FileActions.display() is called implicitly
|
||||
fileList.add(fileData);
|
||||
|
||||
var model = fileList.getModelForFile('testName.txt');
|
||||
|
||||
fileActions.registerDefaultActions();
|
||||
fileActions.triggerAction('Download', model, fileList);
|
||||
|
||||
expect(busyStub.calledOnce).toEqual(true);
|
||||
expect(busyStub.calledWith('testName.txt', true)).toEqual(true);
|
||||
expect(handleDownloadStub.calledOnce).toEqual(true);
|
||||
expect(handleDownloadStub.getCall(0).args[0]).toEqual(
|
||||
OC.getRootPath() + '/remote.php/webdav/subdir/testName.txt'
|
||||
);
|
||||
busyStub.reset();
|
||||
handleDownloadStub.yield();
|
||||
|
||||
expect(busyStub.calledOnce).toEqual(true);
|
||||
expect(busyStub.calledWith('testName.txt', false)).toEqual(true);
|
||||
|
||||
busyStub.restore();
|
||||
handleDownloadStub.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('download spinner', function() {
|
||||
var FileActions = OCA.Files.FileActions;
|
||||
var $el;
|
||||
|
||||
beforeEach(function() {
|
||||
$el = $('<a href="#"><span class="icon icon-download"></span><span>Download</span></a>');
|
||||
});
|
||||
|
||||
it('replaces download icon with spinner', function() {
|
||||
FileActions.updateFileActionSpinner($el, true);
|
||||
expect($el.find('.icon.icon-loading-small').length).toEqual(1);
|
||||
expect($el.find('.icon.icon-download').hasClass('hidden')).toEqual(true);
|
||||
});
|
||||
it('replaces spinner back with download icon with spinner', function() {
|
||||
FileActions.updateFileActionSpinner($el, true);
|
||||
FileActions.updateFileActionSpinner($el, false);
|
||||
expect($el.find('.icon.icon-loading-small').length).toEqual(0);
|
||||
expect($el.find('.icon.icon-download').hasClass('hidden')).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,344 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
describe('OCA.Files.FileActionsMenu tests', function() {
|
||||
var fileList, fileActions, menu, actionStub, menuContext, $tr;
|
||||
|
||||
beforeEach(function() {
|
||||
// init horrible parameters
|
||||
var $body = $('#testArea');
|
||||
$body.append('<input type="hidden" id="permissions" value="31"></input>');
|
||||
// dummy files table
|
||||
actionStub = sinon.stub();
|
||||
fileActions = new OCA.Files.FileActions();
|
||||
fileList = new OCA.Files.FileList($body, {
|
||||
fileActions: fileActions
|
||||
});
|
||||
fileList.changeDirectory('/subdir', false, true);
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'Testdropdown',
|
||||
displayName: 'Testdropdowndisplay',
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
icon: function () {
|
||||
return OC.imagePath('core', 'actions/download');
|
||||
},
|
||||
actionHandler: actionStub
|
||||
});
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'Testdropdownnoicon',
|
||||
displayName: 'Testdropdowndisplaynoicon',
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
actionHandler: actionStub
|
||||
});
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'Testinline',
|
||||
displayName: 'Testinlinedisplay',
|
||||
type: OCA.Files.FileActions.TYPE_INLINE,
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'Testdefault',
|
||||
displayName: 'Testdefaultdisplay',
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
fileActions.setDefault('all', 'Testdefault');
|
||||
|
||||
var fileData = {
|
||||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456'
|
||||
};
|
||||
$tr = fileList.add(fileData);
|
||||
|
||||
menuContext = {
|
||||
$file: $tr,
|
||||
fileList: fileList,
|
||||
fileActions: fileActions,
|
||||
dir: fileList.getCurrentDirectory()
|
||||
};
|
||||
menu = new OCA.Files.FileActionsMenu();
|
||||
menu.show(menuContext);
|
||||
});
|
||||
afterEach(function() {
|
||||
fileActions = null;
|
||||
fileList.destroy();
|
||||
fileList = undefined;
|
||||
menu.remove();
|
||||
$('#permissions, .files-filestable').remove();
|
||||
});
|
||||
|
||||
describe('rendering', function() {
|
||||
it('renders dropdown actions in menu', function() {
|
||||
var $action = menu.$el.find('a[data-action=Testdropdown]');
|
||||
expect($action.length).toEqual(1);
|
||||
expect($action.find('img').attr('src'))
|
||||
.toEqual(OC.imagePath('core', 'actions/download'));
|
||||
expect($action.find('.no-icon').length).toEqual(0);
|
||||
|
||||
$action = menu.$el.find('a[data-action=Testdropdownnoicon]');
|
||||
expect($action.length).toEqual(1);
|
||||
expect($action.find('img').length).toEqual(0);
|
||||
expect($action.find('.no-icon').length).toEqual(1);
|
||||
});
|
||||
it('does not render default actions', function() {
|
||||
expect(menu.$el.find('a[data-action=Testdefault]').length).toEqual(0);
|
||||
});
|
||||
it('render inline actions', function() {
|
||||
expect(menu.$el.find('a[data-action=Testinline]').length).toEqual(1);
|
||||
});
|
||||
it('render inline actions but it is hidden', function() {
|
||||
expect(menu.$el.find('a[data-action=Testinline]').parent().hasClass('hidden')).toEqual(true);
|
||||
});
|
||||
it('only renders actions relevant to the mime type', function() {
|
||||
fileActions.registerAction({
|
||||
name: 'Match',
|
||||
displayName: 'MatchDisplay',
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
fileActions.registerAction({
|
||||
name: 'Nomatch',
|
||||
displayName: 'NoMatchDisplay',
|
||||
mime: 'application/octet-stream',
|
||||
permissions: OC.PERMISSION_READ
|
||||
});
|
||||
|
||||
menu.render();
|
||||
expect(menu.$el.find('a[data-action=Match]').length).toEqual(1);
|
||||
expect(menu.$el.find('a[data-action=NoMatch]').length).toEqual(0);
|
||||
});
|
||||
it('only renders actions relevant to the permissions', function() {
|
||||
fileActions.registerAction({
|
||||
name: 'Match',
|
||||
displayName: 'MatchDisplay',
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_UPDATE
|
||||
});
|
||||
fileActions.registerAction({
|
||||
name: 'Nomatch',
|
||||
displayName: 'NoMatchDisplay',
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_DELETE
|
||||
});
|
||||
|
||||
menu.render();
|
||||
expect(menu.$el.find('a[data-action=Match]').length).toEqual(1);
|
||||
expect(menu.$el.find('a[data-action=NoMatch]').length).toEqual(0);
|
||||
});
|
||||
it('sorts by order attribute, then name', function() {
|
||||
fileActions.registerAction({
|
||||
name: 'Baction',
|
||||
displayName: 'Baction',
|
||||
order: 2,
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
});
|
||||
fileActions.registerAction({
|
||||
name: 'Zaction',
|
||||
displayName: 'Zaction',
|
||||
order: 1,
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
});
|
||||
fileActions.registerAction({
|
||||
name: 'Yaction',
|
||||
displayName: 'Yaction',
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
});
|
||||
fileActions.registerAction({
|
||||
name: 'Waction',
|
||||
displayName: 'Waction',
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
});
|
||||
|
||||
menu.render();
|
||||
var zactionIndex = menu.$el.find('a[data-action=Zaction]').closest('li').index();
|
||||
var bactionIndex = menu.$el.find('a[data-action=Baction]').closest('li').index();
|
||||
expect(zactionIndex).toBeLessThan(bactionIndex);
|
||||
|
||||
var wactionIndex = menu.$el.find('a[data-action=Waction]').closest('li').index();
|
||||
var yactionIndex = menu.$el.find('a[data-action=Yaction]').closest('li').index();
|
||||
expect(wactionIndex).toBeLessThan(yactionIndex);
|
||||
});
|
||||
it('calls displayName function', function() {
|
||||
var displayNameStub = sinon.stub().returns('Test');
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'Something',
|
||||
displayName: displayNameStub,
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_ALL
|
||||
});
|
||||
|
||||
menu.render();
|
||||
|
||||
expect(displayNameStub.calledOnce).toEqual(true);
|
||||
expect(displayNameStub.calledWith(menuContext)).toEqual(true);
|
||||
expect(menu.$el.find('a[data-action=Something] span:not(.icon)').text()).toEqual('Test');
|
||||
});
|
||||
it('uses plain iconClass', function() {
|
||||
fileActions.registerAction({
|
||||
name: 'Something',
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_ALL,
|
||||
iconClass: 'test'
|
||||
});
|
||||
|
||||
menu.render();
|
||||
|
||||
expect(menu.$el.find('a[data-action=Something]').children('span.icon').hasClass('test')).toEqual(true);
|
||||
});
|
||||
it('calls iconClass function', function() {
|
||||
var iconClassStub = sinon.stub().returns('test');
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'Something',
|
||||
mime: 'text/plain',
|
||||
permissions: OC.PERMISSION_ALL,
|
||||
iconClass: iconClassStub
|
||||
});
|
||||
|
||||
menu.render();
|
||||
|
||||
expect(iconClassStub.calledOnce).toEqual(true);
|
||||
expect(iconClassStub.calledWith(menuContext.$file.attr('data-file'), menuContext)).toEqual(true);
|
||||
expect(menu.$el.find('a[data-action=Something]').children('span.icon').hasClass('test')).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('action handler', function() {
|
||||
it('calls action handler when clicking menu item', function() {
|
||||
var $action = menu.$el.find('a[data-action=Testdropdown]');
|
||||
$action.click();
|
||||
|
||||
expect(actionStub.calledOnce).toEqual(true);
|
||||
expect(actionStub.getCall(0).args[0]).toEqual('testName.txt');
|
||||
expect(actionStub.getCall(0).args[1].$file[0]).toEqual($tr[0]);
|
||||
expect(actionStub.getCall(0).args[1].fileList).toEqual(fileList);
|
||||
expect(actionStub.getCall(0).args[1].fileActions).toEqual(fileActions);
|
||||
expect(actionStub.getCall(0).args[1].dir).toEqual('/subdir');
|
||||
});
|
||||
});
|
||||
describe('default actions from registerDefaultActions', function() {
|
||||
beforeEach(function() {
|
||||
fileActions.clear();
|
||||
fileActions.registerDefaultActions();
|
||||
});
|
||||
it('redirects to download URL when clicking download', function() {
|
||||
var redirectStub = sinon.stub(OC, 'redirect');
|
||||
var fileData = {
|
||||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456'
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
fileActions.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
var menuContext = {
|
||||
$file: $tr,
|
||||
fileList: fileList,
|
||||
fileActions: fileActions,
|
||||
fileInfoModel: new OCA.Files.FileInfoModel(fileData),
|
||||
dir: fileList.getCurrentDirectory()
|
||||
};
|
||||
menu = new OCA.Files.FileActionsMenu();
|
||||
menu.show(menuContext);
|
||||
|
||||
menu.$el.find('.action-download').click();
|
||||
|
||||
expect(redirectStub.calledOnce).toEqual(true);
|
||||
expect(redirectStub.getCall(0).args[0]).toContain(
|
||||
OC.getRootPath() +
|
||||
'/remote.php/webdav/subdir/testName.txt'
|
||||
);
|
||||
redirectStub.restore();
|
||||
});
|
||||
it('takes the file\'s path into account when clicking download', function() {
|
||||
var redirectStub = sinon.stub(OC, 'redirect');
|
||||
var fileData = {
|
||||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
path: '/anotherpath/there',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456'
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
fileActions.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
var menuContext = {
|
||||
$file: $tr,
|
||||
fileList: fileList,
|
||||
fileActions: fileActions,
|
||||
fileInfoModel: new OCA.Files.FileInfoModel(fileData),
|
||||
dir: '/anotherpath/there'
|
||||
};
|
||||
menu = new OCA.Files.FileActionsMenu();
|
||||
menu.show(menuContext);
|
||||
|
||||
menu.$el.find('.action-download').click();
|
||||
|
||||
expect(redirectStub.calledOnce).toEqual(true);
|
||||
expect(redirectStub.getCall(0).args[0]).toContain(
|
||||
OC.getRootPath() + '/remote.php/webdav/anotherpath/there/testName.txt'
|
||||
);
|
||||
redirectStub.restore();
|
||||
});
|
||||
it('deletes file when clicking delete', function() {
|
||||
var deleteStub = sinon.stub(fileList, 'do_delete');
|
||||
var fileData = {
|
||||
id: 18,
|
||||
type: 'file',
|
||||
name: 'testName.txt',
|
||||
path: '/somepath/dir',
|
||||
mimetype: 'text/plain',
|
||||
size: '1234',
|
||||
etag: 'a01234c',
|
||||
mtime: '123456'
|
||||
};
|
||||
var $tr = fileList.add(fileData);
|
||||
fileActions.display($tr.find('td.filename'), true, fileList);
|
||||
|
||||
var menuContext = {
|
||||
$file: $tr,
|
||||
fileList: fileList,
|
||||
fileActions: fileActions,
|
||||
fileInfoModel: new OCA.Files.FileInfoModel(fileData),
|
||||
dir: '/somepath/dir'
|
||||
};
|
||||
menu = new OCA.Files.FileActionsMenu();
|
||||
menu.show(menuContext);
|
||||
|
||||
menu.$el.find('.action-delete').click();
|
||||
|
||||
expect(deleteStub.calledOnce).toEqual(true);
|
||||
expect(deleteStub.getCall(0).args[0]).toEqual('testName.txt');
|
||||
expect(deleteStub.getCall(0).args[1]).toEqual('/somepath/dir');
|
||||
deleteStub.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,129 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
describe('OCA.Files.Files tests', function() {
|
||||
var Files = OCA.Files.Files;
|
||||
|
||||
describe('File name validation', function() {
|
||||
it('Validates correct file names', function() {
|
||||
var fileNames = [
|
||||
'boringname',
|
||||
'something.with.extension',
|
||||
'now with spaces',
|
||||
'.a',
|
||||
'..a',
|
||||
'.dotfile',
|
||||
'single\'quote',
|
||||
' spaces before',
|
||||
'spaces after ',
|
||||
'allowed chars including the crazy ones $%&_-^@!,()[]{}=;#',
|
||||
'汉字也能用',
|
||||
'und Ümläüte sind auch willkommen'
|
||||
];
|
||||
for ( var i = 0; i < fileNames.length; i++ ) {
|
||||
var error = false;
|
||||
try {
|
||||
expect(Files.isFileNameValid(fileNames[i])).toEqual(true);
|
||||
}
|
||||
catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toEqual(false);
|
||||
}
|
||||
});
|
||||
it('Detects invalid file names', function() {
|
||||
var fileNames = [
|
||||
'',
|
||||
' ',
|
||||
'.',
|
||||
'..',
|
||||
' ..',
|
||||
'.. ',
|
||||
'. ',
|
||||
' .',
|
||||
'foo.part',
|
||||
'bar.filepart'
|
||||
];
|
||||
for ( var i = 0; i < fileNames.length; i++ ) {
|
||||
var threwException = false;
|
||||
try {
|
||||
Files.isFileNameValid(fileNames[i]);
|
||||
console.error('Invalid file name not detected:', fileNames[i]);
|
||||
}
|
||||
catch (e) {
|
||||
threwException = true;
|
||||
}
|
||||
expect(threwException).toEqual(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('getDownloadUrl', function() {
|
||||
it('returns the ajax download URL when filename and dir specified', function() {
|
||||
var url = Files.getDownloadUrl('test file.txt', '/subdir');
|
||||
expect(url).toEqual(OC.getRootPath() + '/remote.php/webdav/subdir/test%20file.txt');
|
||||
});
|
||||
it('returns the webdav download URL when filename and root dir specified', function() {
|
||||
var url = Files.getDownloadUrl('test file.txt', '/');
|
||||
expect(url).toEqual(OC.getRootPath() + '/remote.php/webdav/test%20file.txt');
|
||||
});
|
||||
it('returns the ajax download URL when multiple files specified', function() {
|
||||
var url = Files.getDownloadUrl(['test file.txt', 'abc.txt'], '/subdir');
|
||||
expect(url).toEqual(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22test%20file.txt%22%2C%22abc.txt%22%5D');
|
||||
});
|
||||
});
|
||||
describe('handleDownload', function() {
|
||||
var redirectStub;
|
||||
var cookieStub;
|
||||
var clock;
|
||||
var testUrl;
|
||||
|
||||
beforeEach(function() {
|
||||
testUrl = 'http://example.com/owncloud/path/download.php';
|
||||
redirectStub = sinon.stub(OC, 'redirect');
|
||||
cookieStub = sinon.stub(OC.Util, 'isCookieSetToValue');
|
||||
clock = sinon.useFakeTimers();
|
||||
});
|
||||
afterEach(function() {
|
||||
redirectStub.restore();
|
||||
cookieStub.restore();
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('appends secret to url when no existing parameters', function() {
|
||||
Files.handleDownload(testUrl);
|
||||
expect(redirectStub.calledOnce).toEqual(true);
|
||||
expect(redirectStub.getCall(0).args[0]).toContain(testUrl + '?downloadStartSecret=');
|
||||
});
|
||||
it('appends secret to url with existing parameters', function() {
|
||||
Files.handleDownload(testUrl + '?test=1');
|
||||
expect(redirectStub.calledOnce).toEqual(true);
|
||||
expect(redirectStub.getCall(0).args[0]).toContain(testUrl + '?test=1&downloadStartSecret=');
|
||||
});
|
||||
it('sets cookie and calls callback when cookie appears', function() {
|
||||
var callbackStub = sinon.stub();
|
||||
var token;
|
||||
Files.handleDownload(testUrl, callbackStub);
|
||||
expect(redirectStub.calledOnce).toEqual(true);
|
||||
token = OC.parseQueryString(redirectStub.getCall(0).args[0]).downloadStartSecret;
|
||||
expect(token).toBeDefined();
|
||||
|
||||
expect(cookieStub.calledOnce).toEqual(true);
|
||||
cookieStub.returns(false);
|
||||
clock.tick(600);
|
||||
|
||||
expect(cookieStub.calledTwice).toEqual(true);
|
||||
expect(cookieStub.getCall(1).args[0]).toEqual('ocDownloadStarted');
|
||||
expect(cookieStub.getCall(1).args[1]).toEqual(token);
|
||||
expect(callbackStub.notCalled).toEqual(true);
|
||||
|
||||
cookieStub.returns(true);
|
||||
clock.tick(2000);
|
||||
|
||||
expect(cookieStub.callCount).toEqual(3);
|
||||
expect(callbackStub.calledOnce).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,227 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
describe('OCA.Files.FileSummary tests', function() {
|
||||
var FileSummary = OCA.Files.FileSummary;
|
||||
var $container;
|
||||
|
||||
beforeEach(function() {
|
||||
$container = $('<table><tr></tr></table>').find('tr');
|
||||
});
|
||||
afterEach(function() {
|
||||
$container = null;
|
||||
});
|
||||
|
||||
it('renders summary as text', function() {
|
||||
var s = new FileSummary($container);
|
||||
s.setSummary({
|
||||
totalDirs: 5,
|
||||
totalFiles: 2,
|
||||
totalSize: 256000
|
||||
});
|
||||
expect($container.hasClass('hidden')).toEqual(false);
|
||||
expect($container.find('.dirinfo').text()).toEqual('5 folders');
|
||||
expect($container.find('.fileinfo').text()).toEqual('2 files');
|
||||
expect($container.find('.filesize').text()).toEqual('250 KB');
|
||||
});
|
||||
it('hides summary when no files or folders', function() {
|
||||
var s = new FileSummary($container);
|
||||
s.setSummary({
|
||||
totalDirs: 0,
|
||||
totalFiles: 0,
|
||||
totalSize: 0
|
||||
});
|
||||
expect($container.hasClass('hidden')).toEqual(true);
|
||||
});
|
||||
it('increases summary when adding files', function() {
|
||||
var s = new FileSummary($container);
|
||||
s.setSummary({
|
||||
totalDirs: 5,
|
||||
totalFiles: 2,
|
||||
totalSize: 256000
|
||||
});
|
||||
s.add({type: 'file', size: 256000});
|
||||
s.add({type: 'dir', size: 100});
|
||||
s.update();
|
||||
expect($container.hasClass('hidden')).toEqual(false);
|
||||
expect($container.find('.dirinfo').text()).toEqual('6 folders');
|
||||
expect($container.find('.fileinfo').text()).toEqual('3 files');
|
||||
expect($container.find('.filesize').text()).toEqual('500 KB');
|
||||
expect(s.summary.totalDirs).toEqual(6);
|
||||
expect(s.summary.totalFiles).toEqual(3);
|
||||
expect(s.summary.totalSize).toEqual(512100);
|
||||
});
|
||||
it('decreases summary when removing files', function() {
|
||||
var s = new FileSummary($container);
|
||||
s.setSummary({
|
||||
totalDirs: 5,
|
||||
totalFiles: 2,
|
||||
totalSize: 256000
|
||||
});
|
||||
s.remove({type: 'file', size: 128000});
|
||||
s.remove({type: 'dir', size: 100});
|
||||
s.update();
|
||||
expect($container.hasClass('hidden')).toEqual(false);
|
||||
expect($container.find('.dirinfo').text()).toEqual('4 folders');
|
||||
expect($container.find('.fileinfo').text()).toEqual('1 file');
|
||||
expect($container.find('.filesize').text()).toEqual('125 KB');
|
||||
expect(s.summary.totalDirs).toEqual(4);
|
||||
expect(s.summary.totalFiles).toEqual(1);
|
||||
expect(s.summary.totalSize).toEqual(127900);
|
||||
});
|
||||
|
||||
it('renders filtered summary as text', function() {
|
||||
var s = new FileSummary($container);
|
||||
s.setSummary({
|
||||
totalDirs: 5,
|
||||
totalFiles: 2,
|
||||
totalSize: 256000,
|
||||
filter: 'foo'
|
||||
});
|
||||
expect($container.hasClass('hidden')).toEqual(false);
|
||||
expect($container.find('.dirinfo').text()).toEqual('5 folders');
|
||||
expect($container.find('.fileinfo').text()).toEqual('2 files');
|
||||
expect($container.find('.filter').text()).toEqual(' match "foo"');
|
||||
expect($container.find('.filesize').text()).toEqual('250 KB');
|
||||
});
|
||||
it('hides filtered summary when no files or folders', function() {
|
||||
var s = new FileSummary($container);
|
||||
s.setSummary({
|
||||
totalDirs: 0,
|
||||
totalFiles: 0,
|
||||
totalSize: 0,
|
||||
filter: 'foo'
|
||||
});
|
||||
expect($container.hasClass('hidden')).toEqual(true);
|
||||
});
|
||||
it('increases filtered summary when adding files', function() {
|
||||
var s = new FileSummary($container);
|
||||
s.setSummary({
|
||||
totalDirs: 5,
|
||||
totalFiles: 2,
|
||||
totalSize: 256000,
|
||||
filter: 'foo'
|
||||
});
|
||||
s.add({name: 'bar.txt', type: 'file', size: 256000});
|
||||
s.add({name: 'foo.txt', type: 'file', size: 256001});
|
||||
s.add({name: 'bar', type: 'dir', size: 100});
|
||||
s.add({name: 'foo', type: 'dir', size: 102});
|
||||
s.update();
|
||||
expect($container.hasClass('hidden')).toEqual(false);
|
||||
expect($container.find('.dirinfo').text()).toEqual('6 folders');
|
||||
expect($container.find('.fileinfo').text()).toEqual('3 files');
|
||||
expect($container.find('.filter').text()).toEqual(' match "foo"');
|
||||
expect($container.find('.filesize').text()).toEqual('500 KB');
|
||||
expect(s.summary.totalDirs).toEqual(6);
|
||||
expect(s.summary.totalFiles).toEqual(3);
|
||||
expect(s.summary.totalSize).toEqual(512103);
|
||||
});
|
||||
it('decreases filtered summary when removing files', function() {
|
||||
var s = new FileSummary($container);
|
||||
s.setSummary({
|
||||
totalDirs: 5,
|
||||
totalFiles: 2,
|
||||
totalSize: 256000,
|
||||
filter: 'foo'
|
||||
});
|
||||
s.remove({name: 'bar.txt', type: 'file', size: 128000});
|
||||
s.remove({name: 'foo.txt', type: 'file', size: 127999});
|
||||
s.remove({name: 'bar', type: 'dir', size: 100});
|
||||
s.remove({name: 'foo', type: 'dir', size: 98});
|
||||
s.update();
|
||||
expect($container.hasClass('hidden')).toEqual(false);
|
||||
expect($container.find('.dirinfo').text()).toEqual('4 folders');
|
||||
expect($container.find('.fileinfo').text()).toEqual('1 file');
|
||||
expect($container.find('.filter').text()).toEqual(' match "foo"');
|
||||
expect($container.find('.filesize').text()).toEqual('125 KB');
|
||||
expect(s.summary.totalDirs).toEqual(4);
|
||||
expect(s.summary.totalFiles).toEqual(1);
|
||||
expect(s.summary.totalSize).toEqual(127903);
|
||||
});
|
||||
it('properly sum up pending folder sizes after adding', function() {
|
||||
var s = new FileSummary($container);
|
||||
s.setSummary({
|
||||
totalDirs: 0,
|
||||
totalFiles: 0,
|
||||
totalSize: 0
|
||||
});
|
||||
s.add({type: 'dir', size: -1});
|
||||
s.update();
|
||||
expect($container.hasClass('hidden')).toEqual(false);
|
||||
expect($container.find('.dirinfo').text()).toEqual('1 folder');
|
||||
expect($container.find('.fileinfo').hasClass('hidden')).toEqual(true);
|
||||
expect($container.find('.filesize').text()).toEqual('Pending');
|
||||
expect(s.summary.totalDirs).toEqual(1);
|
||||
expect(s.summary.totalFiles).toEqual(0);
|
||||
expect(s.summary.totalSize).toEqual(0);
|
||||
});
|
||||
it('properly sum up pending folder sizes after remove', function() {
|
||||
var s = new FileSummary($container);
|
||||
s.setSummary({
|
||||
totalDirs: 0,
|
||||
totalFiles: 0,
|
||||
totalSize: 0
|
||||
});
|
||||
s.add({type: 'dir', size: -1});
|
||||
s.remove({type: 'dir', size: -1});
|
||||
s.update();
|
||||
expect($container.hasClass('hidden')).toEqual(true);
|
||||
expect(s.summary.totalDirs).toEqual(0);
|
||||
expect(s.summary.totalFiles).toEqual(0);
|
||||
expect(s.summary.totalSize).toEqual(0);
|
||||
});
|
||||
describe('hidden files', function() {
|
||||
var config;
|
||||
var summary;
|
||||
|
||||
beforeEach(function() {
|
||||
config = new OC.Backbone.Model();
|
||||
summary = new FileSummary($container, {
|
||||
config: config
|
||||
});
|
||||
});
|
||||
|
||||
it('renders hidden count section when hidden files are hidden', function() {
|
||||
window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
|
||||
|
||||
summary.add({name: 'abc', type: 'file', size: 256000});
|
||||
summary.add({name: 'def', type: 'dir', size: 100});
|
||||
summary.add({name: '.hidden', type: 'dir', size: 512000});
|
||||
summary.update();
|
||||
expect($container.hasClass('hidden')).toEqual(false);
|
||||
expect($container.find('.dirinfo').text()).toEqual('2 folders');
|
||||
expect($container.find('.fileinfo').text()).toEqual('1 file');
|
||||
expect($container.find('.hiddeninfo').hasClass('hidden')).toEqual(false);
|
||||
expect($container.find('.hiddeninfo').text()).toEqual(' (including 1 hidden)');
|
||||
expect($container.find('.filesize').text()).toEqual('750 KB');
|
||||
});
|
||||
it('does not render hidden count section when hidden files exist but are visible', function() {
|
||||
window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: true });
|
||||
|
||||
summary.add({name: 'abc', type: 'file', size: 256000});
|
||||
summary.add({name: 'def', type: 'dir', size: 100});
|
||||
summary.add({name: '.hidden', type: 'dir', size: 512000});
|
||||
summary.update();
|
||||
expect($container.hasClass('hidden')).toEqual(false);
|
||||
expect($container.find('.dirinfo').text()).toEqual('2 folders');
|
||||
expect($container.find('.fileinfo').text()).toEqual('1 file');
|
||||
expect($container.find('.hiddeninfo').hasClass('hidden')).toEqual(true);
|
||||
expect($container.find('.filesize').text()).toEqual('750 KB');
|
||||
});
|
||||
it('does not render hidden count section when no hidden files exist', function() {
|
||||
window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
|
||||
|
||||
summary.add({name: 'abc', type: 'file', size: 256000});
|
||||
summary.add({name: 'def', type: 'dir', size: 100});
|
||||
summary.update();
|
||||
expect($container.hasClass('hidden')).toEqual(false);
|
||||
expect($container.find('.dirinfo').text()).toEqual('1 folder');
|
||||
expect($container.find('.fileinfo').text()).toEqual('1 file');
|
||||
expect($container.find('.hiddeninfo').hasClass('hidden')).toEqual(true);
|
||||
expect($container.find('.filesize').text()).toEqual('250 KB');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,271 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
describe('OCA.Files.MainFileInfoDetailView tests', function() {
|
||||
var view, tooltipStub, fileActions, fileList, testFileInfo;
|
||||
|
||||
beforeEach(function() {
|
||||
tooltipStub = sinon.stub($.fn, 'tooltip');
|
||||
fileActions = new OCA.Files.FileActions();
|
||||
fileList = new OCA.Files.FileList($('<table></table>'), {
|
||||
fileActions: fileActions
|
||||
});
|
||||
view = new OCA.Files.MainFileInfoDetailView({
|
||||
fileList: fileList,
|
||||
fileActions: fileActions
|
||||
});
|
||||
testFileInfo = new OCA.Files.FileInfoModel({
|
||||
id: 5,
|
||||
name: 'One.txt',
|
||||
mimetype: 'text/plain',
|
||||
permissions: 31,
|
||||
path: '/subdir',
|
||||
size: 123456789,
|
||||
etag: 'abcdefg',
|
||||
mtime: Date.UTC(2015, 6, 17, 1, 2, 0, 0)
|
||||
});
|
||||
});
|
||||
afterEach(function() {
|
||||
view.remove();
|
||||
view = undefined;
|
||||
tooltipStub.restore();
|
||||
|
||||
});
|
||||
describe('rendering', function() {
|
||||
it('displays basic info', function() {
|
||||
var clock = sinon.useFakeTimers(Date.UTC(2015, 6, 17, 1, 2, 0, 3));
|
||||
var dateExpected = OC.Util.formatDate(Date(Date.UTC(2015, 6, 17, 1, 2, 0, 0)));
|
||||
view.setFileInfo(testFileInfo);
|
||||
expect(view.$el.find('.fileName h3').text()).toEqual('One.txt');
|
||||
expect(view.$el.find('.fileName h3').attr('title')).toEqual('One.txt');
|
||||
expect(view.$el.find('.size').text()).toEqual('117.7 MB');
|
||||
expect(view.$el.find('.size').attr('title')).toEqual('123456789 bytes');
|
||||
expect(view.$el.find('.date').text()).toEqual('seconds ago');
|
||||
expect(view.$el.find('.date').attr('title')).toEqual(dateExpected);
|
||||
clock.restore();
|
||||
});
|
||||
it('displays permalink', function() {
|
||||
view.setFileInfo(testFileInfo);
|
||||
expect(view.$el.find('.permalink').attr('href'))
|
||||
.toEqual(OC.getProtocol() + '://' + OC.getHost() + OC.generateUrl('/f/5'));
|
||||
});
|
||||
it('displays favorite icon', function() {
|
||||
fileActions.registerAction({
|
||||
name: 'Favorite',
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_NONE
|
||||
});
|
||||
|
||||
testFileInfo.set('tags', [OC.TAG_FAVORITE]);
|
||||
view.setFileInfo(testFileInfo);
|
||||
expect(view.$el.find('.action-favorite > span').hasClass('icon-starred')).toEqual(true);
|
||||
expect(view.$el.find('.action-favorite > span').hasClass('icon-star')).toEqual(false);
|
||||
|
||||
testFileInfo.set('tags', []);
|
||||
view.setFileInfo(testFileInfo);
|
||||
expect(view.$el.find('.action-favorite > span').hasClass('icon-starred')).toEqual(false);
|
||||
expect(view.$el.find('.action-favorite > span').hasClass('icon-star')).toEqual(true);
|
||||
});
|
||||
it('does not display favorite icon if favorite action is not available', function() {
|
||||
testFileInfo.set('tags', [OC.TAG_FAVORITE]);
|
||||
view.setFileInfo(testFileInfo);
|
||||
expect(view.$el.find('.action-favorite').length).toEqual(0);
|
||||
|
||||
testFileInfo.set('tags', []);
|
||||
view.setFileInfo(testFileInfo);
|
||||
expect(view.$el.find('.action-favorite').length).toEqual(0);
|
||||
});
|
||||
it('displays mime icon', function() {
|
||||
// File
|
||||
var lazyLoadPreviewStub = sinon.stub(fileList, 'lazyLoadPreview');
|
||||
testFileInfo.set('mimetype', 'text/calendar');
|
||||
view.setFileInfo(testFileInfo);
|
||||
|
||||
expect(lazyLoadPreviewStub.calledOnce).toEqual(true);
|
||||
var previewArgs = lazyLoadPreviewStub.getCall(0).args;
|
||||
expect(previewArgs[0].mime).toEqual('text/calendar');
|
||||
expect(previewArgs[0].path).toEqual('/subdir/One.txt');
|
||||
expect(previewArgs[0].etag).toEqual('abcdefg');
|
||||
|
||||
expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(true);
|
||||
|
||||
// returns mime icon first without img parameter
|
||||
previewArgs[0].callback(
|
||||
OC.imagePath('core', 'filetypes/text-calendar.svg')
|
||||
);
|
||||
|
||||
// still loading
|
||||
expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(true);
|
||||
|
||||
// preview loading failed, no prview
|
||||
previewArgs[0].error();
|
||||
|
||||
// loading stopped, the mimetype icon gets displayed
|
||||
expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(false);
|
||||
expect(view.$el.find('.thumbnail').css('background-image'))
|
||||
.toContain('filetypes/text-calendar.svg');
|
||||
|
||||
// Folder
|
||||
testFileInfo.set('mimetype', 'httpd/unix-directory');
|
||||
view.setFileInfo(testFileInfo);
|
||||
|
||||
expect(view.$el.find('.thumbnail').css('background-image'))
|
||||
.toContain('filetypes/folder.svg');
|
||||
|
||||
lazyLoadPreviewStub.restore();
|
||||
});
|
||||
it('uses icon from model if present in model', function() {
|
||||
var lazyLoadPreviewStub = sinon.stub(fileList, 'lazyLoadPreview');
|
||||
testFileInfo.set('mimetype', 'httpd/unix-directory');
|
||||
testFileInfo.set('icon', OC.MimeType.getIconUrl('dir-external'));
|
||||
view.setFileInfo(testFileInfo);
|
||||
|
||||
expect(lazyLoadPreviewStub.notCalled).toEqual(true);
|
||||
|
||||
expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(false);
|
||||
expect(view.$el.find('.thumbnail').css('background-image'))
|
||||
.toContain('filetypes/folder-external.svg');
|
||||
|
||||
lazyLoadPreviewStub.restore();
|
||||
});
|
||||
it('displays thumbnail', function() {
|
||||
var lazyLoadPreviewStub = sinon.stub(fileList, 'lazyLoadPreview');
|
||||
|
||||
testFileInfo.set('mimetype', 'text/plain');
|
||||
view.setFileInfo(testFileInfo);
|
||||
|
||||
expect(lazyLoadPreviewStub.calledOnce).toEqual(true);
|
||||
var previewArgs = lazyLoadPreviewStub.getCall(0).args;
|
||||
expect(previewArgs[0].mime).toEqual('text/plain');
|
||||
expect(previewArgs[0].path).toEqual('/subdir/One.txt');
|
||||
expect(previewArgs[0].etag).toEqual('abcdefg');
|
||||
|
||||
expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(true);
|
||||
|
||||
// returns mime icon first without img parameter
|
||||
previewArgs[0].callback(
|
||||
OC.imagePath('core', 'filetypes/text-plain.svg')
|
||||
);
|
||||
|
||||
// still loading
|
||||
expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(true);
|
||||
|
||||
// return an actual (simulated) image
|
||||
previewArgs[0].callback(
|
||||
'testimage', {
|
||||
width: 100,
|
||||
height: 200
|
||||
}
|
||||
);
|
||||
|
||||
// loading stopped, image got displayed
|
||||
expect(view.$el.find('.thumbnail').css('background-image'))
|
||||
.toContain('testimage');
|
||||
|
||||
expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(false);
|
||||
|
||||
lazyLoadPreviewStub.restore();
|
||||
});
|
||||
it('does not show size if no size available', function() {
|
||||
testFileInfo.unset('size');
|
||||
view.setFileInfo(testFileInfo);
|
||||
|
||||
expect(view.$el.find('.size').length).toEqual(0);
|
||||
});
|
||||
it('renders displayName instead of name if available', function() {
|
||||
testFileInfo.set('displayName', 'hello.txt');
|
||||
view.setFileInfo(testFileInfo);
|
||||
|
||||
expect(view.$el.find('.fileName h3').text()).toEqual('hello.txt');
|
||||
expect(view.$el.find('.fileName h3').attr('title')).toEqual('hello.txt');
|
||||
});
|
||||
it('rerenders when changes are made on the model', function() {
|
||||
// Show the "Favorite" icon
|
||||
fileActions.registerAction({
|
||||
name: 'Favorite',
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_NONE
|
||||
});
|
||||
|
||||
view.setFileInfo(testFileInfo);
|
||||
|
||||
testFileInfo.set('tags', [OC.TAG_FAVORITE]);
|
||||
|
||||
expect(view.$el.find('.action-favorite > span').hasClass('icon-starred')).toEqual(true);
|
||||
expect(view.$el.find('.action-favorite > span').hasClass('icon-star')).toEqual(false);
|
||||
|
||||
testFileInfo.set('tags', []);
|
||||
|
||||
expect(view.$el.find('.action-favorite > span').hasClass('icon-starred')).toEqual(false);
|
||||
expect(view.$el.find('.action-favorite > span').hasClass('icon-star')).toEqual(true);
|
||||
});
|
||||
it('unbinds change listener from model', function() {
|
||||
// Show the "Favorite" icon
|
||||
fileActions.registerAction({
|
||||
name: 'Favorite',
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_NONE
|
||||
});
|
||||
|
||||
view.setFileInfo(testFileInfo);
|
||||
view.setFileInfo(new OCA.Files.FileInfoModel({
|
||||
id: 999,
|
||||
name: 'test.txt',
|
||||
path: '/'
|
||||
}));
|
||||
|
||||
// set value on old model
|
||||
testFileInfo.set('tags', [OC.TAG_FAVORITE]);
|
||||
|
||||
// no change
|
||||
expect(view.$el.find('.action-favorite > span').hasClass('icon-starred')).toEqual(false);
|
||||
expect(view.$el.find('.action-favorite > span').hasClass('icon-star')).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe('events', function() {
|
||||
it('triggers default action when clicking on the thumbnail', function() {
|
||||
var actionHandler = sinon.stub();
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'Something',
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
actionHandler: actionHandler
|
||||
});
|
||||
fileActions.setDefault('text/plain', 'Something');
|
||||
|
||||
view.setFileInfo(testFileInfo);
|
||||
|
||||
view.$el.find('.thumbnail').click();
|
||||
|
||||
expect(actionHandler.calledOnce).toEqual(true);
|
||||
expect(actionHandler.getCall(0).args[0]).toEqual('One.txt');
|
||||
expect(actionHandler.getCall(0).args[1].fileList).toEqual(fileList);
|
||||
expect(actionHandler.getCall(0).args[1].fileActions).toEqual(fileActions);
|
||||
expect(actionHandler.getCall(0).args[1].fileInfoModel).toEqual(testFileInfo);
|
||||
});
|
||||
it('triggers "Favorite" action when clicking on the star', function() {
|
||||
var actionHandler = sinon.stub();
|
||||
|
||||
fileActions.registerAction({
|
||||
name: 'Favorite',
|
||||
mime: 'all',
|
||||
permissions: OC.PERMISSION_READ,
|
||||
actionHandler: actionHandler
|
||||
});
|
||||
|
||||
view.setFileInfo(testFileInfo);
|
||||
|
||||
view.$el.find('.action-favorite').click();
|
||||
|
||||
expect(actionHandler.calledOnce).toEqual(true);
|
||||
expect(actionHandler.getCall(0).args[0]).toEqual('One.txt');
|
||||
expect(actionHandler.getCall(0).args[1].fileList).toEqual(fileList);
|
||||
expect(actionHandler.getCall(0).args[1].fileActions).toEqual(fileActions);
|
||||
expect(actionHandler.getCall(0).args[1].fileInfoModel).toEqual(testFileInfo);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
describe('OCA.Files.NewFileMenu', function() {
|
||||
var FileList = OCA.Files.FileList;
|
||||
var menu, fileList, $uploadField, $trigger;
|
||||
|
||||
beforeEach(function() {
|
||||
// dummy upload button
|
||||
var $container = $('<div id="app-content-files"></div>');
|
||||
$uploadField = $('<input id="file_upload_start"></input>');
|
||||
$trigger = $('<a href="#">Menu</a>');
|
||||
$container.append($uploadField).append($trigger);
|
||||
$('#testArea').append($container);
|
||||
|
||||
fileList = new FileList($container);
|
||||
menu = new OCA.Files.NewFileMenu({
|
||||
fileList: fileList
|
||||
});
|
||||
menu.showAt($trigger);
|
||||
});
|
||||
afterEach(function() {
|
||||
OC.hideMenus();
|
||||
fileList = null;
|
||||
menu = null;
|
||||
});
|
||||
|
||||
describe('rendering', function() {
|
||||
it('renders menu items', function() {
|
||||
var $items = menu.$el.find('.menuitem');
|
||||
expect($items.length).toEqual(2);
|
||||
// label points to the file_upload_start item
|
||||
var $item = $items.eq(0);
|
||||
expect($item.is('label')).toEqual(true);
|
||||
expect($item.attr('for')).toEqual('file_upload_start');
|
||||
});
|
||||
});
|
||||
describe('New file/folder', function() {
|
||||
var $input;
|
||||
var createDirectoryStub;
|
||||
|
||||
beforeEach(function() {
|
||||
createDirectoryStub = sinon.stub(FileList.prototype, 'createDirectory');
|
||||
menu.$el.find('.menuitem').eq(1).click();
|
||||
$input = menu.$el.find('form.filenameform input');
|
||||
});
|
||||
afterEach(function() {
|
||||
createDirectoryStub.restore();
|
||||
});
|
||||
|
||||
it('sets default text in field', function() {
|
||||
// text + submit
|
||||
expect($input.length).toEqual(2);
|
||||
expect($input.val()).toEqual('New folder');
|
||||
});
|
||||
it('prevents entering invalid file names', function() {
|
||||
$input.val('..');
|
||||
$input.trigger(new $.Event('keyup', {keyCode: 13}));
|
||||
$input.closest('form').submit();
|
||||
|
||||
expect(createDirectoryStub.notCalled).toEqual(true);
|
||||
});
|
||||
it('prevents entering file names that already exist', function() {
|
||||
var inListStub = sinon.stub(fileList, 'inList').returns(true);
|
||||
$input.val('existing.txt');
|
||||
$input.trigger(new $.Event('keyup', {keyCode: 13}));
|
||||
$input.closest('form').submit();
|
||||
|
||||
expect(createDirectoryStub.notCalled).toEqual(true);
|
||||
inListStub.restore();
|
||||
});
|
||||
it('creates directory when clicking on create directory field', function() {
|
||||
$input = menu.$el.find('form.filenameform input');
|
||||
$input.val('some folder');
|
||||
$input.trigger(new $.Event('keyup', {keyCode: 13}));
|
||||
$input.closest('form').submit();
|
||||
|
||||
expect(createDirectoryStub.calledOnce).toEqual(true);
|
||||
expect(createDirectoryStub.getCall(0).args[0]).toEqual('some folder');
|
||||
});
|
||||
});
|
||||
describe('custom entries', function() {
|
||||
var oldPlugins;
|
||||
var plugin;
|
||||
var actionStub;
|
||||
|
||||
beforeEach(function() {
|
||||
oldPlugins = _.extend({}, OC.Plugins._plugins);
|
||||
actionStub = sinon.stub();
|
||||
plugin = {
|
||||
attach: function(menu) {
|
||||
menu.addMenuEntry({
|
||||
id: 'file',
|
||||
displayName: t('files_texteditor', 'Text file'),
|
||||
templateName: t('files_texteditor', 'New text file.txt'),
|
||||
iconClass: 'icon-filetype-text',
|
||||
fileType: 'file',
|
||||
actionHandler: actionStub
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
OC.Plugins.register('OCA.Files.NewFileMenu', plugin);
|
||||
menu = new OCA.Files.NewFileMenu({
|
||||
fileList: fileList
|
||||
});
|
||||
menu.showAt($trigger);
|
||||
});
|
||||
afterEach(function() {
|
||||
OC.Plugins._plugins = oldPlugins;
|
||||
});
|
||||
it('renders custom menu items', function() {
|
||||
expect(menu.$el.find('.menuitem').length).toEqual(3);
|
||||
expect(menu.$el.find('.menuitem[data-action=file]').length).toEqual(1);
|
||||
});
|
||||
it('calls action handler when clicking on custom item', function() {
|
||||
menu.$el.find('.menuitem').eq(2).click();
|
||||
var $input = menu.$el.find('form.filenameform input');
|
||||
$input.val('some name');
|
||||
$input.trigger(new $.Event('keyup', {keyCode: 13}));
|
||||
$input.closest('form').submit();
|
||||
|
||||
expect(actionStub.calledOnce).toEqual(true);
|
||||
expect(actionStub.getCall(0).args[0]).toEqual('some name');
|
||||
});
|
||||
it('switching fields removes the previous form', function() {
|
||||
menu.$el.find('.menuitem').eq(2).click();
|
||||
expect(menu.$el.find('form').length).toEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
describe('OCA.Files.TagsPlugin tests', function() {
|
||||
var fileList;
|
||||
var testFiles;
|
||||
|
||||
beforeEach(function() {
|
||||
var $content = $('<div id="app-content"></div>');
|
||||
$('#testArea').append($content);
|
||||
// dummy file list
|
||||
var $div = $(
|
||||
'<div>' +
|
||||
'<table class="files-filestable">' +
|
||||
'<thead></thead>' +
|
||||
'<tbody class="files-fileList"></tbody>' +
|
||||
'</table>' +
|
||||
'</div>');
|
||||
$('#app-content').append($div);
|
||||
|
||||
fileList = new OCA.Files.FileList($div);
|
||||
OCA.Files.TagsPlugin.attach(fileList);
|
||||
|
||||
testFiles = [{
|
||||
id: 1,
|
||||
type: 'file',
|
||||
name: 'One.txt',
|
||||
path: '/subdir',
|
||||
mimetype: 'text/plain',
|
||||
size: 12,
|
||||
permissions: OC.PERMISSION_ALL,
|
||||
etag: 'abc',
|
||||
shareOwner: 'User One',
|
||||
isShareMountPoint: false,
|
||||
tags: ['tag1', 'tag2']
|
||||
}];
|
||||
});
|
||||
afterEach(function() {
|
||||
fileList.destroy();
|
||||
fileList = null;
|
||||
});
|
||||
|
||||
describe('Favorites icon', function() {
|
||||
it('renders favorite icon and extra data', function() {
|
||||
var $favoriteMark, $tr;
|
||||
fileList.setFiles(testFiles);
|
||||
$tr = fileList.$el.find('tbody tr:first');
|
||||
$favoriteMark = $tr.find('.favorite-mark');
|
||||
expect($favoriteMark.length).toEqual(1);
|
||||
expect($favoriteMark.hasClass('permanent')).toEqual(false);
|
||||
|
||||
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2']);
|
||||
expect($tr.attr('data-favorite')).not.toBeDefined();
|
||||
});
|
||||
it('renders permanent favorite icon and extra data', function() {
|
||||
var $favoriteMark, $tr;
|
||||
testFiles[0].tags.push(OC.TAG_FAVORITE);
|
||||
fileList.setFiles(testFiles);
|
||||
$tr = fileList.$el.find('tbody tr:first');
|
||||
$favoriteMark = $tr.find('.favorite-mark');
|
||||
expect($favoriteMark.length).toEqual(1);
|
||||
expect($favoriteMark.hasClass('permanent')).toEqual(true);
|
||||
|
||||
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', OC.TAG_FAVORITE]);
|
||||
expect($tr.attr('data-favorite')).toEqual('true');
|
||||
});
|
||||
});
|
||||
describe('Applying tags', function() {
|
||||
it('through FileActionsMenu sends request to server and updates icon', function(done) {
|
||||
var request;
|
||||
fileList.setFiles(testFiles);
|
||||
var $tr = fileList.findFileEl('One.txt');
|
||||
var $favoriteMark = $tr.find('.favorite-mark');
|
||||
var $showMenuAction = $tr.find('.action-menu');
|
||||
$showMenuAction.click();
|
||||
var $favoriteActionInMenu = $tr.find('.fileActionsMenu .action-favorite');
|
||||
$favoriteActionInMenu.click();
|
||||
|
||||
expect(fakeServer.requests.length).toEqual(1);
|
||||
request = fakeServer.requests[0];
|
||||
expect(JSON.parse(request.requestBody)).toEqual({
|
||||
tags: ['tag1', 'tag2', OC.TAG_FAVORITE]
|
||||
});
|
||||
request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
|
||||
tags: ['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]
|
||||
}));
|
||||
|
||||
setTimeout(function () {
|
||||
// re-read the element as it was re-inserted
|
||||
$tr = fileList.findFileEl('One.txt');
|
||||
$favoriteMark = $tr.find('.favorite-mark');
|
||||
$showMenuAction = $tr.find('.action-menu');
|
||||
|
||||
expect($tr.attr('data-favorite')).toEqual('true');
|
||||
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]);
|
||||
expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]);
|
||||
expect($favoriteMark.find('.icon').hasClass('icon-star')).toEqual(false);
|
||||
expect($favoriteMark.find('.icon').hasClass('icon-starred')).toEqual(true);
|
||||
|
||||
// show again the menu and get the new action, as the menu was
|
||||
// closed and removed (and with it, the previous action) when that
|
||||
// action was clicked
|
||||
$showMenuAction.click();
|
||||
$favoriteActionInMenu = $tr.find('.fileActionsMenu .action-favorite');
|
||||
$favoriteActionInMenu.click();
|
||||
|
||||
setTimeout(function() {
|
||||
expect(fakeServer.requests.length).toEqual(2);
|
||||
request = fakeServer.requests[1];
|
||||
expect(JSON.parse(request.requestBody)).toEqual({
|
||||
tags: ['tag1', 'tag2', 'tag3']
|
||||
});
|
||||
|
||||
request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
|
||||
tags: ['tag1', 'tag2', 'tag3']
|
||||
}));
|
||||
|
||||
setTimeout(function() {
|
||||
// re-read the element as it was re-inserted
|
||||
$tr = fileList.findFileEl('One.txt');
|
||||
$favoriteMark = $tr.find('.favorite-mark');
|
||||
|
||||
expect($tr.attr('data-favorite')).toBeFalsy();
|
||||
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3']);
|
||||
expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3']);
|
||||
expect($favoriteMark.find('.icon').hasClass('icon-star')).toEqual(true);
|
||||
expect($favoriteMark.find('.icon').hasClass('icon-starred')).toEqual(false);
|
||||
|
||||
done();
|
||||
}, 1);
|
||||
}, 1);
|
||||
}, 1);
|
||||
});
|
||||
});
|
||||
describe('elementToFile', function() {
|
||||
it('returns tags', function() {
|
||||
fileList.setFiles(testFiles);
|
||||
var $tr = fileList.findFileEl('One.txt');
|
||||
var data = fileList.elementToFile($tr);
|
||||
expect(data.tags).toEqual(['tag1', 'tag2']);
|
||||
});
|
||||
it('returns empty array when no tags present', function() {
|
||||
delete testFiles[0].tags;
|
||||
fileList.setFiles(testFiles);
|
||||
var $tr = fileList.findFileEl('One.txt');
|
||||
var data = fileList.elementToFile($tr);
|
||||
expect(data.tags).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -101,7 +101,6 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
|
|||
\OCP\Util::addInitScript(Application::APP_ID, 'init');
|
||||
\OCP\Util::addInitScript(Application::APP_ID, 'init-public');
|
||||
\OCP\Util::addScript('files', 'main');
|
||||
\OCP\Util::addStyle('files', 'merged');
|
||||
|
||||
// Add file-request script if needed
|
||||
$attributes = $share->getAttributes();
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ style('settings', 'settings');
|
|||
script('settings', 'settings');
|
||||
\OCP\Util::addScript('settings', 'legacy-admin');
|
||||
script('core', 'setupchecks');
|
||||
script('files', 'jquery.fileupload');
|
||||
|
||||
?>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,6 @@ cd $REPODIR
|
|||
# Settings
|
||||
node node_modules/handlebars/bin/handlebars -n OC.Settings.Templates apps/settings/js/templates -f apps/settings/js/templates.js
|
||||
|
||||
# Files app
|
||||
node node_modules/handlebars/bin/handlebars -n OCA.Files.Templates apps/files/js/templates -f apps/files/js/templates.js
|
||||
|
||||
# Sharing
|
||||
node node_modules/handlebars/bin/handlebars -n OCA.Sharing.Templates apps/files_sharing/js/templates -f apps/files_sharing/js/templates.js
|
||||
|
||||
# Files external
|
||||
node node_modules/handlebars/bin/handlebars -n OCA.Files_External.Templates apps/files_external/js/templates -f apps/files_external/js/templates.js
|
||||
|
||||
|
|
@ -26,6 +20,5 @@ if [[ $(git diff --name-only) ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
echo "All up to date! Carry on :D"
|
||||
exit 0
|
||||
|
|
|
|||
|
|
@ -772,7 +772,7 @@ const Dialogs = {
|
|||
var defer = $.Deferred()
|
||||
if (!this.$fileexistsTemplate) {
|
||||
var self = this
|
||||
$.get(OC.filePath('files', 'templates', 'fileexists.html'), function(tmpl) {
|
||||
$.get(OC.filePath('core', 'templates/legacy', 'fileexists.html'), function(tmpl) {
|
||||
self.$fileexistsTemplate = $(tmpl)
|
||||
defer.resolve(self.$fileexistsTemplate)
|
||||
})
|
||||
|
|
|
|||
4
dist/core-login.js
vendored
4
dist/core-login.js
vendored
File diff suppressed because one or more lines are too long
2
dist/core-login.js.map
vendored
2
dist/core-login.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/core-main.js
vendored
4
dist/core-main.js
vendored
File diff suppressed because one or more lines are too long
2
dist/core-main.js.map
vendored
2
dist/core-main.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files-settings-personal.js
vendored
4
dist/files-settings-personal.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files-settings-personal.js.map
vendored
2
dist/files-settings-personal.js.map
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue