/**
 * -------------------------------------------------------------------------------------------
 * Copyright (c) Microsoft Corporation.  All Rights Reserved.  Licensed under the MIT License.
 * See License in the project root for license information.
 * -------------------------------------------------------------------------------------------
 */ import { customElement , html , property , TemplateResult } from 'lit-element' ; import { styles } from './mgt-file-upload-css' ; import { strings } from './strings' ; import { getSvg , SvgIcon } from '../../../utils/SvgHelper' ; import { formatBytes } from '../../../utils/Utils' ; import { IGraph , MgtBaseComponent } from '@microsoft/mgt-element' ; import { ViewType } from '../../../graph/types' ; import { DriveItem } from '@microsoft/microsoft-graph-types' ; import { clearFilesCache , getGraphfile , getUploadSession , sendFileContent , sendFileChunck , deleteSessionFile } from '../../../graph/graph.files' ; export { FluentProgress , FluentButton , FluentCheckbox , FluentCard } from '@fluentui/web-components' ; /**
 * Upload conflict behavior status
 */ export const enum MgtFileUploadConflictBehavior { rename , replace } export interface export interface MgtFileUploadItem { /**
     * Session url to keep upload progress open untill all chuncks are sent
     */ uploadUrl ? : string ; /**
     *  Upload file progress value
     */ percent ? : number ; /**
     *  Validate if File has any conflict Behavior
     */ conflictBehavior ? : MgtFileUploadConflictBehavior ; /**
     *  Output "Success" or "Fail" icon base on upload response
     */ iconStatus ? : TemplateResult ; /**
     * File object to be upload.
     */ file ? : File ; /**
     *  Full file Path to be upload.
     */ fullPath ? : string ; /**
     * Mgt-File View state change on upload response
     */ view ? : ViewType ; /**
     * Manipulate fileDetails on upload lifecycle
     */ driveItem ? : DriveItem ; /**
     * Mgt-File line2Property output field message
     */ fieldUploadResponse ? : string ; /**
     * Validates state of upload progress
     */ completed ? : boolean ; /**
     * Load large Files into ArrayBuffer to send by chuncks
     */ mimeStreamString ? : ArrayBuffer ; /**
     *  Max chunck size to upload file by slice
     */ maxSize ? : number ; /**
     *  Minimal chunck size to upload file by slice
     */ minSize ? : number ; } export interface export interface MgtFileUploadConfig { type { IGraph } graph : IGraph ; type { string } siteId ? : string ; type { string } driveId ? : string ; type { string } groupId ? : string ; type { string } itemId ? : string ; type { string } itemPath ? : string ; type { string } userId ? : string ; type { Number } maxFileSize ? : number ; type { Number } maxUploadFile ? : Number ; type { string [ ] } excludedFileExtensions ? : string [ ] ; } export class extends MgtBaseComponent cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop cssprop @ customElement ( 'mgt-file-upload' ) export class MgtFileUpload extends MgtBaseComponent { /**
     * Array of styles to apply to the element. The styles should be defined
     * using the `css` tag function.
     */ static get styles ( ) { return styles ; } protected get strings ( ) { return strings ; } type { MgtFileUploadItem [ ] } memberof @ property ( { type : Object } ) public filesToUpload : MgtFileUploadItem [ ] ; type { MgtFileUploadConfig } memberof @ property ( { type : Object } ) public fileUploadList : MgtFileUploadConfig ; static return { * } memberof public static get requiredScopes ( ) : string [ ] { return [ ... new Set ( [ 'files.readwrite' , 'files.readwrite.all' , 'sites.readwrite.all' ] ) ] ; } private _dragCounter : number = 0 ; private _dropEffect : string = 'copy' ; private _maxChunckSize : number = 4 * 1024 * 1024 ; private _dialogTitle : string = '' ; private _dialogContent : string = '' ; private _dialogPrimaryButton : string = '' ; private _dialogSecondaryButton : string = '' ; private _dialogCheckBox : string = '' ; private _applyAll : boolean = false ; private _applyAllConflitBehavior : number = null ; private _maximumFiles : boolean = false ; private _maximumFileSize : boolean = false ; private _excludedFileType : boolean = false ; constructor ( ) { super ( ) ; this . filesToUpload = [ ] ; } returns public render ( ) { if ( this . parentElement !== null ) { const root = this . parentElement ; root . addEventListener ( 'dragenter' , this . handleonDragEnter ) ; root . addEventListener ( 'dragleave' , this . handleonDragLeave ) ; root . addEventListener ( 'dragover' , this . handleonDragOver ) ; root . addEventListener ( 'drop' , this . handleonDrop ) ; } return html `
        <div id="file-upload-dialog" class="file-upload-dialog">
          <!-- Modal content -->
          <fluent-card class="file-upload-dialog-content">
            <span class="file-upload-dialog-close" id="file-upload-dialog-close" >${ getSvg ( SvgIcon . Cancel ) }</span>
            <div class="file-upload-dialog-content-text">
              <h2 class="file-upload-dialog-title">${ this . _dialogTitle }</h2>
              <div>${ this . _dialogContent }</div>
              <div class="file-upload-dialog-check-wrapper">
                <fluent-checkbox id="file-upload-dialog-check" class="file-upload-dialog-check" >
                  <span>${ this . _dialogCheckBox }</span>
                </fluent-checkbox>
              </div>
            </div>
            <div class="file-upload-dialog-editor">
              <fluent-button class="file-upload-dialog-ok">
              ${ this . _dialogPrimaryButton }
              </fluent-button>
              <fluent-button class="file-upload-dialog-cancel">
              ${ this . _dialogSecondaryButton }
              </fluent-button>
            </div>
          </fluent-card>
        </div>
        <div id="file-upload-border" >
        </div>
        <div class="file-upload-area-button">
        <div>
          <input
            id="file-upload-input"
            type="file"
            multiple="true"
            @change="${ this . onFileUploadChange }"
          />
          <fluent-button class="file-upload-button" @click=${ this . onFileUploadClick }>
            ${ getSvg ( SvgIcon . Upload ) }${ strings . buttonUploadFile }
          </fluent-button>
        </div>
        </div>
        <div class="file-upload-Template">
        ${ this . renderFolderTemplate ( this . filesToUpload ) }
        </div>
       ` ; } param fileItems returns protected renderFolderTemplate ( fileItems ) { let folderStructure = [ ] ; if ( fileItems . length > 0 ) { const TemplateFileItems = fileItems . map ( fileItem => { if ( folderStructure . indexOf ( fileItem . fullPath . substring ( 0 , fileItem . fullPath . lastIndexOf ( '/' ) ) ) === - 1 ) { if ( fileItem . fullPath . substring ( 0 , fileItem . fullPath . lastIndexOf ( '/' ) ) !== '' ) { folderStructure . push ( fileItem . fullPath . substring ( 0 , fileItem . fullPath . lastIndexOf ( '/' ) ) ) ; return html `
            <div class='file-upload-table'>
              <div class='file-upload-cell'>
                <mgt-file
                  .fileDetails=${ { name : fileItem . fullPath . substring ( 1 , fileItem . fullPath . lastIndexOf ( '/' ) ) , folder : 'Folder' } }
                  .view=${ ViewType . oneline }
                  class="mgt-file-item"
                >
                </mgt-file>
              </div>
            </div>
            ${ this . renderFileTemplate ( fileItem , 'file-upload-folder-tab' ) }` ; } else { return html `${ this . renderFileTemplate ( fileItem , '' ) }` ; } } else { return html `${ this . renderFileTemplate ( fileItem , 'file-upload-folder-tab' ) }` ; } } ) ; return html `${ TemplateFileItems }` ; } else { return null ; } } param fileItem returns protected renderFileTemplate ( fileItem , folderTabStyle ) { return html `
        <div class="${ fileItem . completed ? 'file-upload-table' : 'file-upload-table upload' }">
          <div class="${ folderTabStyle + ( fileItem . fieldUploadResponse === 'lastModifiedDateTime' ? ' file-upload-dialog-success' : '' ) }">
            <div class='file-upload-cell'>
              <div style=${ fileItem . fieldUploadResponse === 'description' ? 'opacity: 0.5;' : '' }>
                <div class="file-upload-status">
                  ${ fileItem . iconStatus }
                </div>
                <mgt-file
                  .fileDetails=${ fileItem . driveItem }
                  .view=${ fileItem . view }
                  .line2Property=${ fileItem . fieldUploadResponse }
                  class="mgt-file-item"
                  >
                </mgt-file>
              </div>
            </div>
              ${ fileItem . completed === false ? this . renderFileUploadTemplate ( fileItem ) : null }
            </div>
          </div>
        </div>
        ` ; } param fileItem returns protected renderFileUploadTemplate ( fileItem ) { return html `
    <div class='file-upload-cell'>
      <div class='file-upload-table file-upload-name' >
        <div class='file-upload-cell'>
          <div title="${ fileItem . file . name }" class='file-upload-filename'>${ fileItem . file . name }</div>
        </div>
      </div>
      <div class='file-upload-table'>
        <div class='file-upload-cell'>
          <div class="${ fileItem . completed ? 'file-upload-table' : 'file-upload-table upload' }">
            <fluent-progress class="file-upload-bar" value="${ fileItem . percent }" ></fluent-progress>
            <div class='file-upload-cell' style="padding-left:5px">
              <span>${ fileItem . percent }%</span>
              <span
                class="file-upload-cancel"
                @click=${ e => this . deleteFileUploadSession ( fileItem ) }>
                ${ getSvg ( SvgIcon . Cancel ) }
              </span>
            </div>
          <div>
        </div>
      </div>
    </div>
    ` ; } param event returns protected async onFileUploadChange ( event ) { if ( ! event || event . target . files . length < 1 ) { return ; } else { this . getSelectedFiles ( await this . getFilesFromUploadArea ( event . target . files ) ) ; event . target . value = null ; } } /**
     * Handle the click event on upload file button that open select files dialog to upload.
     *
     */ protected onFileUploadClick ( ) { const uploadInput = this . renderRoot . querySelector ( '#file-upload-input' ) ; uploadInput . click ( ) ; } param fileItem protected async deleteFileUploadSession ( fileItem ) { try { if ( fileItem . uploadUrl !== undefined ) { const response = await deleteSessionFile ( this . fileUploadList . graph , fileItem . uploadUrl ) ; fileItem . uploadUrl = undefined ; fileItem . completed = true ; this . setUploadFail ( fileItem , strings . cancelUploadFile ) ; } else { fileItem . uploadUrl = undefined ; fileItem . completed = true ; this . setUploadFail ( fileItem , strings . cancelUploadFile ) ; } } catch { fileItem . uploadUrl = undefined ; fileItem . completed = true ; this . setUploadFail ( fileItem , strings . cancelUploadFile ) ; } } param event protected handleonDragOver = async ( event ) => { event . preventDefault ( ) ; event . stopPropagation ( ) ; if ( event . dataTransfer . items && event . dataTransfer . items . length > 0 ) { event . dataTransfer . dropEffect = event . dataTransfer . dropEffect = this . _dropEffect ; } } ; param event protected handleonDragEnter = async ( event ) => { event . preventDefault ( ) ; event . stopPropagation ( ) ; this . _dragCounter ++ ; if ( event . dataTransfer . items && event . dataTransfer . items . length > 0 ) { event . dataTransfer . dropEffect = this . _dropEffect ; const dragFileBorder = this . renderRoot . querySelector ( '#file-upload-border' ) ; dragFileBorder . classList . add ( 'visible' ) ; } } ; param event protected handleonDragLeave = event => { event . preventDefault ( ) ; event . stopPropagation ( ) ; this . _dragCounter -- ; if ( this . _dragCounter === 0 ) { const dragFileBorder = this . renderRoot . querySelector ( '#file-upload-border' ) ; dragFileBorder . classList . remove ( 'visible' ) ; } } ; param event protected handleonDrop = async ( event ) => { event . preventDefault ( ) ; event . stopPropagation ( ) ; const dragFileBorder = this . renderRoot . querySelector ( '#file-upload-border' ) ; dragFileBorder . classList . remove ( 'visible' ) ; if ( event . dataTransfer && event . dataTransfer . items ) { this . getSelectedFiles ( await this . getFilesFromUploadArea ( event . dataTransfer . items ) ) ; } event . dataTransfer . clearData ( ) ; this . _dragCounter = 0 ; } ; param inputFiles protected async getSelectedFiles ( files ) { let fileItems = [ ] ; let fileItemsCompleted = [ ] ; this . _applyAll = false ; this . _applyAllConflitBehavior = null ; this . _maximumFiles = false ; this . _maximumFileSize = false ; this . _excludedFileType = false ; this . filesToUpload . forEach ( async ( fileItem ) => { if ( ! fileItem . completed ) { fileItems . push ( fileItem ) ; } else { fileItemsCompleted . push ( fileItem ) ; } } ) ; for ( var i = 0 ; i < files . length ; i ++ ) { const file = files [ i ] ; const fullPath = file . fullPath === '' ? '/' + file . name : file . fullPath ; if ( fileItems . filter ( item => item . fullPath === fullPath ) . length === 0 ) { let acceptFile = true ; if ( fileItems . length >= this . fileUploadList . maxUploadFile ) { acceptFile = false ; if ( ! this . _maximumFiles ) { const maximumFiles = await this . getFileUploadStatus ( files [ i ] , fullPath , 'MaxFiles' , this . fileUploadList ) ; if ( maximumFiles !== null ) { if ( maximumFiles [ 0 ] === 0 ) { return null ; } if ( maximumFiles [ 0 ] === 1 ) { this . _maximumFiles = true ; } } } } if ( this . fileUploadList . maxFileSize !== undefined && acceptFile ) { if ( files [ i ] . size > this . fileUploadList . maxFileSize * 1024 ) { acceptFile = false ; if ( this . _maximumFileSize === false ) { const maximumFileSize = await this . getFileUploadStatus ( files [ i ] , fullPath , 'MaxFileSize' , this . fileUploadList ) ; if ( maximumFileSize !== null ) { if ( maximumFileSize [ 0 ] === 1 ) { this . _maximumFileSize = true ; } } } } } if ( this . fileUploadList . excludedFileExtensions !== undefined ) { if ( this . fileUploadList . excludedFileExtensions . length > 0 && acceptFile ) { if ( this . fileUploadList . excludedFileExtensions . filter ( fileExtension => { return files [ i ] . name . toLowerCase ( ) . indexOf ( fileExtension . toLowerCase ( ) ) > - 1 ; } ) . length > 0 ) { acceptFile = false ; if ( this . _excludedFileType === false ) { const excludedFileType = await this . getFileUploadStatus ( files [ i ] , fullPath , 'ExcludedFileType' , this . fileUploadList ) ; if ( excludedFileType !== null ) { if ( excludedFileType [ 0 ] === 1 ) { this . _excludedFileType = true ; } } } } } } if ( acceptFile ) { const conflictBehavior = await this . getFileUploadStatus ( files [ i ] , fullPath , 'Upload' , this . fileUploadList ) ; let _completed = false ; if ( conflictBehavior !== null ) { if ( conflictBehavior [ 0 ] === - 1 ) { _completed = true ; } else { this . _applyAll = Boolean ( conflictBehavior [ 0 ] ) ; this . _applyAllConflitBehavior = conflictBehavior [ 1 ] ? 1 : 0 ; } } fileItems . push ( { file : files [ i ] , driveItem : { name : files [ i ] . name } , fullPath : fullPath , conflictBehavior : conflictBehavior !== null ? ( conflictBehavior [ 1 ] ? 1 : 0 ) : null , iconStatus : null , percent : 1 , view : ViewType . image , completed : _completed , maxSize : this . _maxChunckSize , minSize : 0 } ) ; } } } fileItems = fileItems . sort ( ( firstFile , secondFile ) => { return firstFile . fullPath . substring ( 0 , firstFile . fullPath . lastIndexOf ( '/' ) ) . localeCompare ( secondFile . fullPath . substring ( 0 , secondFile . fullPath . lastIndexOf ( '/' ) ) ) ; } ) ; fileItems . forEach ( fileItem => { if ( fileItemsCompleted . filter ( item => item . fullPath === fileItem . fullPath ) . length !== 0 ) { let index = fileItemsCompleted . findIndex ( item => item . fullPath === fileItem . fullPath ) ; fileItemsCompleted . splice ( index , 1 ) ; } } ) ; fileItems . push ( ... fileItemsCompleted ) ; this . filesToUpload = fileItems ; this . filesToUpload . forEach ( async ( fileItem ) => { await this . sendFileItemGraph ( fileItem ) ; } ) ; } param file returns protected async getFileUploadStatus ( file , fullPath , DialogStatus , fileUploadList ) { const fileUploadDialog = this . renderRoot . querySelector ( '#file-upload-dialog' ) ; switch ( DialogStatus ) { case 'Upload' : const driveItem = await getGraphfile ( this . fileUploadList . graph , `${ this . getGrapQuery ( fullPath ) }?$select=id` ) ; if ( driveItem !== null ) { if ( this . _applyAll === true ) { return [ this . _applyAll , this . _applyAllConflitBehavior ] ; } fileUploadDialog . classList . add ( 'visible' ) ; this . _dialogTitle = strings . fileReplaceTitle ; this . _dialogContent = strings . fileReplace . replace ( '{FileName}' , file . name ) ; this . _dialogCheckBox = strings . checkApplyAll ; this . _dialogPrimaryButton = strings . buttonReplace ; this . _dialogSecondaryButton = strings . buttonKeep ; super . requestStateUpdate ( true ) ; return new Promise < number [ ] > ( async ( resolve ) => { let fileUploadDialogClose = this . renderRoot . querySelector ( '.file-upload-dialog-close' ) ; let fileUploadDialogOk = this . renderRoot . querySelector ( '.file-upload-dialog-ok' ) ; let fileUploadDialogCancel = this . renderRoot . querySelector ( '.file-upload-dialog-cancel' ) ; let fileUploadDialogCheck = this . renderRoot . querySelector ( '#file-upload-dialog-check' ) ; fileUploadDialogCheck . checked = false ; fileUploadDialogCheck . classList . remove ( 'hide' ) ; fileUploadDialogOk . removeEventListener ( 'click' , onOkDialogClick ) ; fileUploadDialogCancel . removeEventListener ( 'click' , onCancelDialogClick ) ; fileUploadDialogClose . removeEventListener ( 'click' , onCloseDialogClick ) ; fileUploadDialogOk . addEventListener ( 'click' , onOkDialogClick ) ; fileUploadDialogCancel . addEventListener ( 'click' , onCancelDialogClick ) ; fileUploadDialogClose . addEventListener ( 'click' , onCloseDialogClick ) ; function onOkDialogClick ( ) { fileUploadDialog . classList . remove ( 'visible' ) ; resolve ( [ fileUploadDialogCheck . checked ? 1 : 0 , MgtFileUploadConflictBehavior . replace ] ) ; } function onCancelDialogClick ( ) { fileUploadDialog . classList . remove ( 'visible' ) ; resolve ( [ fileUploadDialogCheck . checked ? 1 : 0 , MgtFileUploadConflictBehavior . rename ] ) ; } function onCloseDialogClick ( ) { fileUploadDialog . classList . remove ( 'visible' ) ; resolve ( [ - 1 ] ) ; } } ) ; } else { return null ; } break ; case 'MaxFiles' : fileUploadDialog . classList . add ( 'visible' ) ; this . _dialogTitle = strings . maximumFilesTitle ; this . _dialogContent = strings . maximumFiles . split ( '{MaxNumber}' ) . join ( fileUploadList . maxUploadFile . toString ( ) ) ; this . _dialogCheckBox = strings . checkApplyAll ; this . _dialogPrimaryButton = strings . buttonUpload ; this . _dialogSecondaryButton = strings . buttonReselect ; super . requestStateUpdate ( true ) ; return new Promise < number [ ] > ( async ( resolve ) => { let fileUploadDialogOk = this . renderRoot . querySelector ( '.file-upload-dialog-ok' ) ; let fileUploadDialogCancel = this . renderRoot . querySelector ( '.file-upload-dialog-cancel' ) ; let fileUploadDialogClose = this . renderRoot . querySelector ( '.file-upload-dialog-close' ) ; let fileUploadDialogCheck = this . renderRoot . querySelector ( '#file-upload-dialog-check' ) ; fileUploadDialogCheck . checked = false ; fileUploadDialogCheck . classList . add ( 'hide' ) ; fileUploadDialogOk . removeEventListener ( 'click' , onOkDialogClick ) ; fileUploadDialogCancel . removeEventListener ( 'click' , onCancelDialogClick ) ; fileUploadDialogClose . removeEventListener ( 'click' , onCancelDialogClick ) ; fileUploadDialogOk . addEventListener ( 'click' , onOkDialogClick ) ; fileUploadDialogCancel . addEventListener ( 'click' , onCancelDialogClick ) ; fileUploadDialogClose . addEventListener ( 'click' , onCancelDialogClick ) ; function onOkDialogClick ( ) { fileUploadDialog . classList . remove ( 'visible' ) ; resolve ( [ 1 ] ) ; } function onCancelDialogClick ( ) { fileUploadDialog . classList . remove ( 'visible' ) ; resolve ( [ 0 ] ) ; } } ) ; break ; case 'ExcludedFileType' : fileUploadDialog . classList . add ( 'visible' ) ; this . _dialogTitle = strings . fileTypeTitle ; this . _dialogContent = strings . fileType . replace ( '{FileName}' , file . name ) + ' (' + fileUploadList . excludedFileExtensions . join ( ',' ) + ')' ; this . _dialogCheckBox = strings . checkAgain ; this . _dialogPrimaryButton = strings . buttonOk ; this . _dialogSecondaryButton = strings . buttonCancel ; super . requestStateUpdate ( true ) ; return new Promise < number [ ] > ( async ( resolve ) => { let fileUploadDialogOk = this . renderRoot . querySelector ( '.file-upload-dialog-ok' ) ; let fileUploadDialogCancel = this . renderRoot . querySelector ( '.file-upload-dialog-cancel' ) ; let fileUploadDialogClose = this . renderRoot . querySelector ( '.file-upload-dialog-close' ) ; let fileUploadDialogCheck = this . renderRoot . querySelector ( '#file-upload-dialog-check' ) ; fileUploadDialogCheck . checked = false ; fileUploadDialogCheck . classList . remove ( 'hide' ) ; fileUploadDialogOk . removeEventListener ( 'click' , onOkDialogClick ) ; fileUploadDialogCancel . removeEventListener ( 'click' , onCancelDialogClick ) ; fileUploadDialogClose . removeEventListener ( 'click' , onCancelDialogClick ) ; fileUploadDialogOk . addEventListener ( 'click' , onOkDialogClick ) ; fileUploadDialogCancel . addEventListener ( 'click' , onCancelDialogClick ) ; fileUploadDialogClose . addEventListener ( 'click' , onCancelDialogClick ) ; function onOkDialogClick ( ) { fileUploadDialog . classList . remove ( 'visible' ) ; resolve ( [ fileUploadDialogCheck . checked ? 1 : 0 ] ) ; } function onCancelDialogClick ( ) { fileUploadDialog . classList . remove ( 'visible' ) ; resolve ( [ 0 ] ) ; } } ) ; case 'MaxFileSize' : fileUploadDialog . classList . add ( 'visible' ) ; this . _dialogTitle = strings . maximumFileSizeTitle ; this . _dialogContent = strings . maximumFileSize . replace ( '{FileSize}' , formatBytes ( fileUploadList . maxFileSize ) ) . replace ( '{FileName}' , file . name ) + formatBytes ( file . size ) + '.' ; this . _dialogCheckBox = strings . checkAgain ; this . _dialogPrimaryButton = strings . buttonOk ; this . _dialogSecondaryButton = strings . buttonCancel ; super . requestStateUpdate ( true ) ; return new Promise < number [ ] > ( async ( resolve ) => { let fileUploadDialogOk = this . renderRoot . querySelector ( '.file-upload-dialog-ok' ) ; let fileUploadDialogCancel = this . renderRoot . querySelector ( '.file-upload-dialog-cancel' ) ; let fileUploadDialogClose = this . renderRoot . querySelector ( '.file-upload-dialog-close' ) ; let fileUploadDialogCheck = this . renderRoot . querySelector ( '#file-upload-dialog-check' ) ; fileUploadDialogCheck . checked = false ; fileUploadDialogCheck . classList . remove ( 'hide' ) ; fileUploadDialogOk . removeEventListener ( 'click' , onOkDialogClick ) ; fileUploadDialogCancel . removeEventListener ( 'click' , onCancelDialogClick ) ; fileUploadDialogClose . removeEventListener ( 'click' , onCancelDialogClick ) ; fileUploadDialogOk . addEventListener ( 'click' , onOkDialogClick ) ; fileUploadDialogCancel . addEventListener ( 'click' , onCancelDialogClick ) ; fileUploadDialogClose . addEventListener ( 'click' , onCancelDialogClick ) ; function onOkDialogClick ( ) { fileUploadDialog . classList . remove ( 'visible' ) ; resolve ( [ fileUploadDialogCheck . checked ? 1 : 0 ] ) ; } function onCancelDialogClick ( ) { fileUploadDialog . classList . remove ( 'visible' ) ; resolve ( [ 0 ] ) ; } } ) ; default : break ; } } param fileItem returns protected getGrapQuery ( fullPath ) { let itemPath = '' ; if ( this . fileUploadList . itemPath ) { if ( this . fileUploadList . itemPath . length > 0 ) { itemPath = this . fileUploadList . itemPath . substring ( 0 , 1 ) === '/' ? this . fileUploadList . itemPath : '/' + this . fileUploadList . itemPath ; } } if ( this . fileUploadList . userId && this . fileUploadList . itemId ) { return `/users/${ this . fileUploadList . userId }/drive/items/${ this . fileUploadList . itemId }:${ fullPath }` ; } if ( this . fileUploadList . userId && this . fileUploadList . itemPath ) { return `/users/${ this . fileUploadList . userId }/drive/root:${ itemPath }${ fullPath }` ; } if ( this . fileUploadList . groupId && this . fileUploadList . itemId ) { return `/groups/${ this . fileUploadList . groupId }/drive/items/${ this . fileUploadList . itemId }:${ fullPath }` ; } if ( this . fileUploadList . groupId && this . fileUploadList . itemPath ) { return `/groups/${ this . fileUploadList . groupId }/drive/root:${ itemPath }${ fullPath }` ; } if ( this . fileUploadList . driveId && this . fileUploadList . itemId ) { return `/drives/${ this . fileUploadList . driveId }/items/${ this . fileUploadList . itemId }:${ fullPath }` ; } if ( this . fileUploadList . driveId && this . fileUploadList . itemPath ) { return `/drives/${ this . fileUploadList . driveId }/root:${ itemPath }${ fullPath }` ; } if ( this . fileUploadList . siteId && this . fileUploadList . itemId ) { return `/sites/${ this . fileUploadList . siteId }/drive/items/${ this . fileUploadList . itemId }:${ fullPath }` ; } if ( this . fileUploadList . siteId && this . fileUploadList . itemPath ) { return `/sites/${ this . fileUploadList . siteId }/drive/root:${ itemPath }${ fullPath }` ; } if ( this . fileUploadList . itemId ) { return `/me/drive/items/${ this . fileUploadList . itemId }:${ fullPath }` ; } if ( this . fileUploadList . itemPath ) { return `/me/drive/root:${ itemPath }${ fullPath }` ; } return `/me/drive/root:${ fullPath }` ; } param fileUpload returns protected async sendFileItemGraph ( fileItem ) { const graph = this . fileUploadList . graph ; let graphQuery = '' ; if ( fileItem . file . size < this . _maxChunckSize ) { try { if ( ! fileItem . completed ) { if ( fileItem . conflictBehavior === null || fileItem . conflictBehavior === MgtFileUploadConflictBehavior . replace ) { graphQuery = `${ this . getGrapQuery ( fileItem . fullPath ) }:/content` ; } if ( fileItem . conflictBehavior === MgtFileUploadConflictBehavior . rename ) { graphQuery = `${ this . getGrapQuery ( fileItem . fullPath ) }:/content?@microsoft.graph.conflictBehavior=rename` ; } fileItem . driveItem = await sendFileContent ( graph , graphQuery , fileItem . file ) ; if ( fileItem . driveItem !== null ) { this . setUploadSuccess ( fileItem ) ; } else { fileItem . driveItem = { name : fileItem . file . name } ; this . setUploadFail ( fileItem , strings . failUploadFile ) ; } } } catch ( error ) { this . setUploadFail ( fileItem , strings . failUploadFile ) ; } } else { if ( ! fileItem . completed ) { if ( fileItem . uploadUrl === undefined ) { const response = await getUploadSession ( graph , `${ this . getGrapQuery ( fileItem . fullPath ) }:/createUploadSession` , fileItem . conflictBehavior ) ; try { if ( response !== null ) { fileItem . uploadUrl = response . uploadUrl ; const driveItem = await this . sendSessionUrlGraph ( graph , fileItem ) ; if ( driveItem !== null ) { fileItem . driveItem = driveItem ; this . setUploadSuccess ( fileItem ) ; } else { this . setUploadFail ( fileItem , strings . failUploadFile ) ; } } else { this . setUploadFail ( fileItem , strings . failUploadFile ) ; } } catch { } } } } } param Graph param fileItem returns protected async sendSessionUrlGraph ( graph , fileItem ) { while ( fileItem . file . size > fileItem . minSize ) { if ( fileItem . mimeStreamString === undefined ) { fileItem . mimeStreamString = ( await this . readFileContent ( fileItem . file ) ) as ArrayBuffer ; } const fileSlice = new Blob ( [ fileItem . mimeStreamString . slice ( fileItem . minSize , fileItem . maxSize ) ] ) ; fileItem . percent = Math . round ( ( fileItem . maxSize / fileItem . file . size ) * 100 ) ; super . requestStateUpdate ( true ) ; if ( fileItem . uploadUrl !== undefined ) { const response = await sendFileChunck ( graph , fileItem . uploadUrl , `${ fileItem . maxSize - fileItem . minSize }` , `bytes ${ fileItem . minSize }-${ fileItem . maxSize - 1 }/${ fileItem . file . size }` , fileSlice ) ; if ( response === null ) { return null ; } else if ( response . id !== undefined ) { return response as DriveItem ; } else if ( response . nextExpectedRanges !== undefined ) { fileItem . minSize = parseInt ( response . nextExpectedRanges [ 0 ] . split ( '-' ) [ 0 ] ) ; fileItem . maxSize = fileItem . minSize + this . _maxChunckSize ; if ( fileItem . maxSize > fileItem . file . size ) { fileItem . maxSize = fileItem . file . size ; } } } else { return null ; } } } param fileUpload protected setUploadSuccess ( fileUpload ) { fileUpload . percent = 100 ; super . requestStateUpdate ( true ) ; setTimeout ( ( ) => { fileUpload . iconStatus = getSvg ( SvgIcon . Success ) ; fileUpload . view = ViewType . twolines ; fileUpload . fieldUploadResponse = 'lastModifiedDateTime' ; fileUpload . completed = true ; super . requestStateUpdate ( true ) ; clearFilesCache ( ) ; } , 500 ) ; } param fileUpload protected setUploadFail ( fileUpload , errorMessage ) { setTimeout ( ( ) => { fileUpload . iconStatus = getSvg ( SvgIcon . Fail ) ; fileUpload . view = ViewType . twolines ; fileUpload . driveItem . description = errorMessage ; fileUpload . fieldUploadResponse = 'description' ; fileUpload . completed = true ; super . requestStateUpdate ( true ) ; } , 500 ) ; } param file returns protected readFileContent ( file ) { return new Promise < string | ArrayBuffer > ( ( resolve , reject ) => { const myReader = new FileReader ( ) ; myReader . onloadend = e => { resolve ( myReader . result ) ; } ; myReader . onerror = e => { reject ( e ) ; } ; myReader . readAsArrayBuffer ( file ) ; } ) ; } param uploadFilesItems returns protected async getFilesFromUploadArea ( filesItems ) { const folders = [ ] ; let entry ; const collectFilesItems = [ ] ; for ( let i = 0 ; i < filesItems . length ; i ++ ) { const uploadFileItem = filesItems [ i ] ; if ( uploadFileItem . kind === 'file' ) { if ( uploadFileItem . getAsEntry ) { entry = uploadFileItem . getAsEntry ( ) ; if ( entry . isDirectory ) { folders . push ( entry ) ; } else { const file = uploadFileItem . getAsFile ( ) ; if ( file ) { file . fullPath = '' ; collectFilesItems . push ( file ) ; } } } else if ( uploadFileItem . webkitGetAsEntry ) { entry = uploadFileItem . webkitGetAsEntry ( ) ; if ( entry . isDirectory ) { folders . push ( entry ) ; } else { const file = uploadFileItem . getAsFile ( ) ; if ( file ) { file . fullPath = '' ; collectFilesItems . push ( file ) ; } } } else if ( 'function' == typeof uploadFileItem . getAsFile ) { const file = uploadFileItem . getAsFile ( ) ; if ( file ) { file . fullPath = '' ; collectFilesItems . push ( file ) ; } } continue ; } else { const fileItem = uploadFileItem ; if ( fileItem ) { fileItem . fullPath = '' ; collectFilesItems . push ( fileItem ) ; } } } if ( folders . length > 0 ) { const folderFiles = await this . getFolderFiles ( folders ) ; collectFilesItems . push ( ... folderFiles ) ; } return collectFilesItems ; } param folders returns protected getFolderFiles ( folders ) { return new Promise < File [ ] > ( resolve => { let reading = 0 ; const contents = [ ] ; folders . forEach ( entry => { readEntry ( entry , '' ) ; } ) ; function readEntry ( entry , path ) { if ( entry . isDirectory ) { readReaderContent ( entry . createReader ( ) ) ; } else { reading ++ ; entry . file ( file => { reading -- ; file . fullPath = path ; contents . push ( file ) ; if ( reading === 0 ) { resolve ( contents ) ; } } ) ; } } function readReaderContent ( reader ) { reading ++ ; reader . readEntries ( entries => { reading -- ; for ( const entry of entries ) { readEntry ( entry , entry . fullPath ) ; } if ( reading === 0 ) { resolve ( contents ) ; } } ) ; } } ) ; } }