import { IFileManager, ReadArgs, SortOrder, SearchArgs, FileDragEventArgs, BeforeImageLoadEventArgs } from '../base/interface';
import * as CLS from '../base/classes';
import * as events from '../base/constant';
import { read, paste, Search, filter, Download, Delete, isFileSystemData } from '../common/operations';
import { getValue, setValue, isNullOrUndefined as isNOU, matches, select, createElement, Draggable, isNullOrUndefined } from '@syncfusion/ej2-base';
import { closest, DragEventArgs, detach } from '@syncfusion/ej2-base';
import { DataManager, Query } from '@syncfusion/ej2-data';
import { MenuEventArgs } from '@syncfusion/ej2-navigations';
import { createDialog } from '../pop-up/dialog';
import { ColumnModel } from '../models';

/**
 * Utility function to compare two strings in a way similar to Windows Explorer.
 * Files and folders are sorted separately, with folders coming before files.
 *
 * @param {string} reference - The first string to compare. This could be a file or folder name.
 * @param {string} comparer - The second string to compare. This could be a file or folder name.
 * @returns {number} - A negative number if `reference` should come before `comparer`, a positive number if `comparer` should come before `reference`, and 0 if they are considered equal.
 */
export function sortComparer(reference: string, comparer:  string): number {
    // Check if reference and comparer are files or folders
    const referenceIsFile: boolean = /\.\S+/.test(reference);
    const comparerIsFile: boolean = /\.\S+/.test(comparer);
    // If one is a file and the other is a folder, the folder should come first
    if (referenceIsFile && !comparerIsFile) {return 1; }
    if (!referenceIsFile && comparerIsFile) {return -1; }
    const referenceParts: Array<[number, string]> = [];
    const comparerParts: Array<[number, string]> = [];
    (reference + '').replace(/(\d+)|(\D+)/g, function(_: string, $1: number, $2: string): string { referenceParts.push([$1 || Infinity, $2 || '']); return ''; });
    (comparer + '').replace(/(\d+)|(\D+)/g, function(_: string, $1: number, $2: string): string { comparerParts.push([$1 || Infinity, $2 || '']); return ''; });
    // Compare each part of reference and comparer
    while (referenceParts.length && comparerParts.length) {
        const referencePart: [number, string] = referenceParts.shift();
        const comparerPart: [number, string] = comparerParts.shift();
        if (referencePart && comparerPart) {
            const comparisonResult: number =
                referencePart[0] - comparerPart[0] ||
                referencePart[1].localeCompare(comparerPart[1]);
            if (comparisonResult) {return comparisonResult; }
        }
    }
    return referenceParts.length - comparerParts.length;
}
/**
 * Utility file for common actions
 *
 * @param {HTMLLIElement} node - specifies the node.
 * @param {Object} data - specifies the data.
 * @param {IFileManager} instance - specifies the control instance.
 * @returns {void}
 * @private
 */
export function updatePath(node: HTMLLIElement, data: Object, instance: IFileManager): void {
    const text: string = getValue('name', data);
    const id: string = node.getAttribute('data-id');
    const newText: string = isNOU(id) ? text : id;
    instance.setProperties({ path: getPath(node, newText, instance.hasId) }, true);
    instance.pathId = getPathId(node);
    instance.pathNames = getPathNames(node, text);
}
/**
 * Functions for get path in FileManager
 *
 * @param {Element | Node} element - specifies the element.
 * @param {string} text - specifies the text.
 * @param {boolean} hasId - specifies the id.
 * @returns {string} returns the path.
 * @private
 */
export function getPath(element: Element | Node, text: string, hasId: boolean): string {
    const matched: string[] = getParents(<Element>element, text, false, hasId);
    let path: string = '/';
    const len: number = matched.length - (2);
    for (let i: number = len; i >= 0; i--) {
        path += matched[i as number] + '/';
    }
    return path;
}
/**
 * Functions for get path id in FileManager
 *
 * @param {Element} node - specifies the node element.
 * @returns {string[]} returns the path ids.
 * @private
 */
export function getPathId(node: Element): string[] {
    const matched: string[] = getParents(node, node.getAttribute('data-uid'), true);
    const ids: string[] = [];
    for (let i: number = matched.length - 1; i >= 0; i--) {
        ids.push(matched[i as number]);
    }
    return ids;
}

/**
 * Functions for get path names in FileManager
 *
 * @param {Element} element - specifies the node element.
 * @param {string} text - specifies the text.
 * @returns {string[]} returns the path names.
 * @private
 */
export function getPathNames(element: Element, text: string): string[] {
    const matched: string[] = getParents(element, text, false);
    const names: string[] = [];
    for (let i: number = matched.length - 1; i >= 0; i--) {
        names.push(matched[i as number]);
    }
    return names;
}

/**
 * Functions for get path id in FileManager
 *
 * @param {Element} element - specifies the node element.
 * @param {string} text - specifies the text.
 * @param {boolean} isId - specifies the id.
 * @param {boolean} hasId - checks the id exists.
 * @returns {string[]} returns parent element.
 * @private
 */
export function getParents(element: Element, text: string, isId: boolean, hasId?: boolean): string[] {
    const matched: string[] = [text];
    let el: Element = <Element>element.parentNode;
    while (!isNOU(el)) {
        if (matches(el, '.' + CLS.LIST_ITEM)) {
            const parentText: string = isId ? el.getAttribute('data-uid') : (hasId ? el.getAttribute('data-id') :
                select('.' + CLS.LIST_TEXT, el).textContent);
            matched.push(parentText);
        }
        el = <Element>el.parentNode;
        if (el.classList.contains(CLS.TREE_VIEW)) {
            break;
        }
    }
    return matched;
}
/**
 * Functions for generate path
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @returns {void}
 * @private
 */
export function generatePath(parent: IFileManager): void {
    const key: string = parent.hasId ? 'id' : 'name';
    let newPath: string = '/';
    let i: number = 1;
    for (i; i < parent.pathId.length; i++) {
        const data: Object = getValue(parent.pathId[parseInt(i.toString(), 10)], parent.feParent);
        newPath += getValue(key, data) + '/';
    }
    parent.setProperties({ path: newPath }, true);
}

/**
 * Functions for remove active element
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @returns {void}
 * @private
 */
export function removeActive(parent: IFileManager): void {
    if (parent.isCut) {
        removeBlur(parent);
        parent.selectedNodes = [];
        parent.actionRecords = [];
        parent.enablePaste = false;
        parent.notify(events.hidePaste, {});
    }
}

/**
 * Selects active element in File Manager
 *
 * @param {string} action - specifies the action.
 * @param {IFileManager} parent - specifies the parent element.
 * @returns {boolean} - returns active element.
 * @private
 */
export function activeElement(action: string, parent: IFileManager): boolean {
    parent.isSearchCut = false;
    parent.actionRecords = [];
    parent.activeElements = [];
    parent.notify(events.cutCopyInit, {});
    if (parent.activeElements.length === 0) { return false; }
    removeBlur(parent);
    const blurEle: Element[] = parent.activeElements;
    if (parent.activeModule !== 'navigationpane') {
        parent.targetPath = parent.path;
    } else {
        parent.targetPath = getParentPath(parent.path);
    }
    let i: number = 0;
    if (blurEle) {
        getModule(parent, blurEle[0]);
        if (action === 'cut') {
            while (i < blurEle.length) {
                addBlur(blurEle[i as number]);
                i++;
            }
        }
    }
    i = 0;
    parent.selectedNodes = [];
    parent.enablePaste = true;
    parent.notify(events.showPaste, {});
    while (i < parent.activeRecords.length) {
        parent.actionRecords.push(parent.activeRecords[i as number]);
        parent.selectedNodes.push(getValue('name', parent.activeRecords[i as number]));
        i++;
    }
    if ((parent.breadcrumbbarModule.searchObj.element.value !== '' || parent.isFiltered) &&
        parent.activeModule !== 'navigationpane') {
        parent.selectedNodes = [];
        parent.isSearchCut = true;
        let i: number = 0;
        while (i < parent.selectedItems.length) {
            parent.selectedNodes.push(parent.selectedItems[i as number]);
            i++;
        }
    }
    return true;
}
/**
 * Adds blur to the elements
 *
 * @param {Element} nodes - specifies the nodes.
 * @returns {void}
 * @private
 */
export function addBlur(nodes: Element): void {
    nodes.classList.add(CLS.BLUR);
}

/**
 * Removes blur from elements
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {string} hover - specifies the hover string.
 * @returns {void}
 * @private
 */
export function removeBlur(parent?: IFileManager, hover?: string): void {
    const blurEle: NodeListOf<Element> = (!hover) ? parent.element.querySelectorAll('.' + CLS.BLUR) :
        parent.element.querySelectorAll('.' + CLS.HOVER);
    let i: number = 0;
    while (i < blurEle.length) {
        blurEle[i as number].classList.remove((!hover) ? CLS.BLUR : CLS.HOVER);
        i++;
    }
}

/**
 * Gets module name
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {Element} element - specifies the element.
 * @returns {void}
 * @private
 */
export function getModule(parent: IFileManager, element: Element): void {
    if (element) {
        if (element.classList.contains(CLS.ROW)) {
            parent.activeModule = 'detailsview';
        } else if (closest(element, '.' + CLS.LARGE_ICON)) {
            parent.activeModule = 'largeiconsview';
        } else {
            parent.activeModule = 'navigationpane';
        }
    }
}

/**
 * Get all child items
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {string | number} parentId - specifies the parent ID.
 * @returns {Object[]} An array of child items
 * @private
 */
export function getAllChildItems(parent: IFileManager, parentId: number | string ): { [key: string]: Object }[] {
    const children: { [key: string]: Object }[] = parent.fileSystemData.filter(
        (item: { [key: string]: Object }) =>
            String(item.parentId) === String(parentId)
    );
    let allChildren: { [key: string]: Object }[] = [...children];
    children.forEach((child: { [key: string]: Object }) => {
        const childId: number | string = child.id as string | number;
        allChildren = allChildren.concat(getAllChildItems(parent, childId));
    });
    return allChildren;
}

/**
 * Gets module name
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {string} value - specifies the value.
 * @param {boolean} isLayoutChange - specifies the layout change.
 * @returns {void}
 * @private
 */
export function searchWordHandler(parent: IFileManager, value: string, isLayoutChange: boolean): void {
    let searchWord: string;
    if (value.length === 0 && !parent.isFiltered) {
        parent.notify(events.pathColumn, { args: parent });
    }

    if (isFileSystemData(parent)) {
        if (value === '') {
            parent.itemData = parent.fileSystemData;
            read(parent, isLayoutChange ? events.layoutChange : events.search, parent.path);
        }
        else {
            parent.searchSettings.filterType = isNOU(parent.searchSettings.filterType) ? 'contains' : parent.searchSettings.filterType;
            const currData: { [key: string]: Object } = getValue(parent.pathId[parent.pathId.length - 1], parent.feParent);
            const parentId: number | string = getValue('id', currData);
            const filteredData: { [key: string]: Object }[] = getAllChildItems(parent, parentId);
            const data: Object[] = new DataManager(filteredData).
                executeLocal(new Query().where('name', parent.searchSettings.filterType, value, parent.searchSettings.ignoreCase));
            const searchValue: string = parent.searchSettings.ignoreCase ? value.toLowerCase() : value;
            parent.itemData = data;
            Search(parent, isLayoutChange ? events.layoutChange : events.search, parent.path, searchValue,
                   parent.showHiddenItems, !parent.searchSettings.ignoreCase);
        }
        return;
    }
    if (parent.searchSettings.filterType === 'startsWith') {
        searchWord = value + '*';
    } else if (parent.searchSettings.filterType === 'endsWith') {
        searchWord = '*' + value;
    } else {
        searchWord = '*' + value + '*';
    }
    parent.searchWord = searchWord;
    parent.itemData = [getPathObject(parent)];
    if (value.length > 0) {
        const caseSensitive: boolean = parent.searchSettings.ignoreCase;
        const hiddenItems: boolean = parent.showHiddenItems;
        Search(parent, isLayoutChange ? events.layoutChange : events.search, parent.path, searchWord, hiddenItems, !caseSensitive);
    } else {
        if (!parent.isFiltered) {
            if (parent.isSortByClicked) {
                parent.notify(events.layoutChange, { files: (parent.oldView === 'Details') ? parent.detailsviewModule.gridObj.dataSource : parent.largeiconsviewModule.allItems });
                parent.isSortByClicked = false;
            } else {
                read(parent, isLayoutChange ? events.layoutChange : events.search, parent.path);
            }
        } else {
            filter(parent, events.layoutChange);
        }
    }
}

/**
 * Gets updated layout
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {string} view - specifies the view.
 * @returns {void}
 * @private
 */
export function updateLayout(parent: IFileManager, view: string): void {
    parent.oldView = parent.view;
    parent.setProperties({ view: view }, true);
    if (parent.breadcrumbbarModule.searchObj.element.value !== '' || parent.isFiltered) {
        parent.layoutSelectedItems = parent.selectedItems;
    }
    let searchWord: string = '';
    if (parent.breadcrumbbarModule.searchObj.element.value) {
        searchWord = parent.breadcrumbbarModule.searchObj.element.value;
    }
    parent.isLayoutChange = true;
    searchWordHandler(parent, searchWord, true);
}

/* istanbul ignore next */
/**
 * Gets updated layout
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {Element} element - specifies the element.
 * @returns {void}
 * @private
 */
export function getTargetModule(parent: IFileManager, element: Element): void {
    let tartgetModule: string = '';
    if (element) {
        if (closest(element, '.' + CLS.ROOT + '.' + CLS.CONTROL + ' .' + CLS.GRID_CONTENT)) {
            tartgetModule = 'detailsview';
        } else if (closest(element, '.' + CLS.LARGE_ICONS)) {
            tartgetModule = 'largeiconsview';
        } else if (element.classList.contains('e-fullrow') ||
            element.classList.contains('e-icon-expandable')) {
            tartgetModule = 'navigationpane';
        } else if (closest(element, '.e-address-list-item')) {
            tartgetModule = 'breadcrumbbar';
        } else {
            tartgetModule = '';
        }
    }
    parent.targetModule = tartgetModule;
}
/* istanbul ignore next */
/**
 * refresh the layout
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @returns {void}
 * @private
 */
export function refresh(parent: IFileManager): void {
    parent.itemData = [getPathObject(parent)];
    if (!hasReadAccess(parent.itemData[0])) {
        createDeniedDialog(parent, parent.itemData[0], events.permissionRead);
    } else {
        read(parent, events.refreshEnd, parent.path);
    }
}

/**
 * open action in the layout
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @returns {void}
 * @private
 */
export function openAction(parent: IFileManager): void {
    read(parent, events.openEnd, parent.path);
}

/**
 * open action in the layout
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @returns {Object} - returns the path data.
 * @private
 */
export function getPathObject(parent: IFileManager): Object {
    return getValue(parent.pathId[parent.pathId.length - 1], parent.feParent);
}

/**
 * Copy files
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @returns {void}
 * @private
 */
export function copyFiles(parent: IFileManager): void {
    if (!activeElement('copy', parent)) {
        return;
    } else {
        parent.fileAction = 'copy';
    }
}

/**
 * Cut files
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @returns {void}
 * @private
 */
export function cutFiles(parent: IFileManager): void {
    if (!activeElement('cut', parent)) {
        return;
    } else {
        parent.isCut = true;
        parent.fileAction = 'move';
    }
}

/**
 * To add class for fileType
 *
 * @param {Object} file - specifies the file.
 * @returns {string} - returns the file type.
 * @private
 */
export function fileType(file: Object): string {
    const isFile: string = getValue('isFile', file);
    if (!isFile) {
        return CLS.FOLDER;
    }
    const imageFormat: string[] = ['bmp', 'dib', 'jpg', 'jpeg', 'jpe', 'jfif', 'gif', 'tif', 'tiff', 'png', 'ico'];
    const audioFormat: string[] = ['mp3', 'wav', 'aac', 'ogg', 'wma', 'aif', 'fla', 'm4a'];
    const videoFormat: string[] = ['webm', 'mkv', 'flv', 'vob', 'ogv', 'ogg', 'avi', 'wmv', 'mp4', '3gp'];
    const knownFormat: string[] = ['css', 'exe', 'html', 'js', 'msi', 'pdf', 'pptx', 'ppt', 'rar', 'zip', 'txt', 'docx', 'doc',
        'xlsx', 'xls', 'xml', 'rtf', 'php'];
    let filetype: string = getValue('type', file);
    filetype = filetype.toLowerCase();
    if (filetype.indexOf('.') !== -1) {
        filetype = filetype.split('.').join('');
    }
    let iconType: string;
    if (imageFormat.indexOf(filetype) !== -1) {
        iconType = CLS.ICON_IMAGE;
    } else if (audioFormat.indexOf(filetype) !== -1) {
        iconType = CLS.ICON_MUSIC;
    } else if (videoFormat.indexOf(filetype) !== -1) {
        iconType = CLS.ICON_VIDEO;
    } else if (knownFormat.indexOf(filetype) !== -1) {
        iconType = 'e-fe-' + filetype;
    } else {
        iconType = 'e-fe-unknown e-fe-' + filetype;
    }
    return iconType;
}
/* istanbul ignore next */
/**
 * To get the image URL
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {Object} item - specifies the item.
 * @returns {BeforeImageLoadEventArgs} - returns the eventargs.
 * @private
 */
export function getImageUrl(parent: IFileManager, item: Object): BeforeImageLoadEventArgs {
    let imgUrl: string = isFileSystemData(parent) ? getValue('imageUrl', item) : '';
    if (isFileSystemData(parent)) {
        const eventArgs: BeforeImageLoadEventArgs = {
            fileDetails: [item],
            imageUrl: imgUrl,
            useImageAsUrl: true
        };
        parent.trigger('beforeImageLoad', eventArgs);
        return eventArgs;
    }
    const baseUrl: string = parent.ajaxSettings.getImageUrl ? parent.ajaxSettings.getImageUrl : parent.ajaxSettings.url;
    const pathUrl: string = (baseUrl.indexOf('?') !== -1) ? '&path=' : '?path=';
    const fileName: string = encodeURIComponent(getValue('name', item));
    const fPath: string = getValue('filterPath', item);
    if (parent.hasId) {
        const imgId: string = getValue('id', item);
        imgUrl = baseUrl + pathUrl + parent.path + '&id=' + imgId;
    } else if (!isNOU(fPath)) {
        imgUrl = baseUrl + pathUrl + encodeURIComponent(fPath.replace(/\\/g, '/')) + fileName;
    } else {
        imgUrl = baseUrl + pathUrl + parent.path + fileName;
    }
    imgUrl = imgUrl + '&time=' + (new Date().getTime()).toString();
    const data: Object = { 'action': 'getImage', 'path': parent.path + fileName, 'id': getValue('id', item) };
    const ajaxSettings: Object = {
        url: baseUrl,
        type: 'POST',
        contentType: 'application/json',
        responseType: 'blob',
        data: JSON.stringify(data),
        onSuccess: null,
        onFailure: null,
        beforeSend: null
    };
    const eventArgs: BeforeImageLoadEventArgs = {
        fileDetails: [item],
        imageUrl: imgUrl,
        useImageAsUrl: true,
        ajaxSettings: ajaxSettings
    };
    parent.trigger('beforeImageLoad', eventArgs);
    return eventArgs;
}
/* istanbul ignore next */
/**
 * Gets the full path
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {Object} data - specifies the data.
 * @param {string} path - specifies the path.
 * @returns {string} - returns the image url.
 * @private
 */
export function getFullPath(parent: IFileManager, data: Object, path: string): string {
    const filePath: string = getValue(parent.hasId ? 'id' : 'name', data) + '/';
    const fPath: string = getValue(parent.hasId ? 'filterId' : 'filterPath', data);
    if (!isNOU(fPath)) {
        return fPath.replace(/\\/g, '/').replace(/^.*?(?=\/)/, '') + filePath;
    } else {
        return path + filePath;
    }
}
/**
 * Gets the name
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {Object} data - specifies the data.
 * @returns {string} - returns the name.
 * @private
 */
export function getName(parent: IFileManager, data: Object): string {
    let name: string = getValue('name', data);
    let fPath: string = getValue('filterPath', data);
    if ((parent.breadcrumbbarModule.searchObj.element.value !== '' || parent.isFiltered) && !isNOU(fPath)) {
        fPath = fPath.replace(/\\/g, '/');
        name = fPath.replace(parent.path, '') + name;
    }
    return name;
}

/**
 * Gets the name
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {Object[]} items - specifies the item elements.
 * @returns {Object[]} - returns the sorted data.
 * @private
 */
export function getSortedData(parent: IFileManager, items: Object[]): Object[] {
    if (items.length === 0) { return items; }
    let query: Query ;
    if (parent.sortOrder !== 'None' && !isNullOrUndefined(parent.sortOrder)) {
        query = new Query().sortBy(parent.sortBy, parent.sortOrder.toLowerCase(), true).group('isFile');
    } else {
        query = new Query().group('isFile');
    }
    const lists: Object[] = new DataManager(items).executeLocal(query);
    return getValue('records', lists);
}
/**
 * Gets the data object
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {string} key - specifies the key.
 * @param {string} value - specifies the value.
 * @returns {Object} - returns the sorted data.
 * @private
 */
export function getObject(parent: IFileManager, key: string, value: string): Object {
    const currFiles: Object[] = getValue(parent.pathId[parent.pathId.length - 1], parent.feFiles);
    const result: object[] = currFiles.filter((data: { [key: string]: string | number }) => data[key as string].toString() === value);
    return result[0];
}

/**
 * Creates empty element
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {HTMLElement} element - specifies the element.
 * @param {ReadArgs | SearchArgs} args - specifies the args.
 * @returns {void}
 * @private
 */
export function createEmptyElement(parent: IFileManager, element: HTMLElement, args: ReadArgs | SearchArgs): void {
    let top: number;
    const layoutElement: Element = select('#' + parent.element.id + CLS.LAYOUT_ID, parent.element);
    const addressBarHeight: number = (<HTMLElement>select('#' + parent.element.id + CLS.BREADCRUMBBAR_ID, layoutElement)).offsetHeight;
    top = (<HTMLElement>layoutElement).offsetHeight - addressBarHeight;
    if (parent.view === 'Details') {
        top = top - (<HTMLElement>select('.' + CLS.GRID_HEADER, layoutElement)).offsetHeight;
    }
    if (isNOU(element.querySelector('.' + CLS.EMPTY))) {
        const emptyDiv: Element = createElement('div', { className: CLS.EMPTY });
        const emptyFolder: Element = createElement('div', { className: CLS.LARGE_EMPTY_FOLDER });
        const emptyEle: Element = createElement('div', { className: CLS.EMPTY_CONTENT });
        const dragFile: Element = createElement('div', { className: CLS.EMPTY_INNER_CONTENT });
        if (parent.view === 'Details') {
            element.querySelector('.' + CLS.GRID_VIEW).appendChild(emptyDiv);
        } else {
            element.appendChild(emptyDiv);
        }
        emptyDiv.appendChild(emptyFolder);
        emptyDiv.appendChild(emptyEle);
        emptyDiv.appendChild(dragFile);
    }
    if (element.querySelector('.' + CLS.EMPTY)) {
        if (!isNOU(args.error)) {
            element.querySelector('.' + CLS.EMPTY_CONTENT).innerHTML = getLocaleText(parent, 'Access-Denied');
            element.querySelector('.' + CLS.EMPTY_INNER_CONTENT).innerHTML = getLocaleText(parent, 'Access-Details');
        } else if (parent.isFiltered) {
            element.querySelector('.' + CLS.EMPTY_CONTENT).innerHTML = getLocaleText(parent, 'Filter-Empty');
            element.querySelector('.' + CLS.EMPTY_INNER_CONTENT).innerHTML = getLocaleText(parent, 'Filter-Key');
        } else if (parent.breadcrumbbarModule.searchObj.element.value !== '') {
            element.querySelector('.' + CLS.EMPTY_CONTENT).innerHTML = getLocaleText(parent, 'Search-Empty');
            element.querySelector('.' + CLS.EMPTY_INNER_CONTENT).innerHTML = getLocaleText(parent, 'Search-Key');
        } else {
            element.querySelector('.' + CLS.EMPTY_CONTENT).innerHTML = getLocaleText(parent, 'Folder-Empty');
            element.querySelector('.' + CLS.EMPTY_INNER_CONTENT).innerHTML = parent.uploadObj.dropArea == null ? getLocaleText(parent, '') : getLocaleText(parent, 'File-Upload');
        }
    }
    const eDiv: HTMLElement = <HTMLElement>select('.' + CLS.EMPTY, element);
    top = (top - eDiv.offsetHeight) / 2;
    eDiv.style.marginTop = top + 'px';
}

/**
 * Gets the directories
 *
 * @param {Object[]} files - specifies the file object.
 * @returns {Object[]} - returns the sorted data.
 * @private
 */
export function getDirectories(files: Object[]): Object[] {
    return new DataManager(files).executeLocal(new Query().where(events.isFile, 'equal', false, false));
}

/**
 * set the Node ID
 *
 * @param {ReadArgs} result - specifies the result.
 * @param {string} rootId - specifies the rootId.
 * @returns {void}
 * @private
 */
export function setNodeId(result: ReadArgs, rootId: string): void {
    const dirs: Object[] = getDirectories(result.files);
    for (let i: number = 0, len: number = dirs.length; i < len; i++) {
        setValue('_fm_id', rootId + '_' + i, dirs[i as number]);
    }
}

/**
 * set the date object
 *
 * @param {Object[]} args - specifies the file object.
 * @returns {void}
 * @private
 */
export function setDateObject(args: Object[]): void {
    for (let i: number = 0; i < args.length; i++) {
        const createdDate: Date = new Date(getValue('dateCreated', args[i as number]));
        const modifiedDate: Date = new Date(getValue('dateModified', args[i as number]));
        setValue('_fm_created', createdDate, args[i as number]);
        setValue('_fm_modified', modifiedDate, args[i as number]);
    }
}

/**
 * get the locale text
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {string} text - specifies the text.
 * @returns {string} - returns the locale text.
 * @private
 */
export function getLocaleText(parent: IFileManager, text: string): string {
    const locale: string = parent.localeObj.getConstant(text);
    return (locale === '') ? text : locale;
}

/**
 * get the CSS class
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {string} css - specifies the css.
 * @returns {string} - returns the css classes.
 * @private
 */
export function getCssClass(parent: IFileManager, css: string): string {
    let cssClass: string = parent.cssClass;
    cssClass = (isNOU(cssClass) || cssClass === '') ? css : (cssClass + ' ' + css);
    return cssClass;
}

/**
 * sort on click
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {MenuEventArgs} args - specifies the menu event arguements.
 * @returns {void}
 * @private
 */
export function sortbyClickHandler(parent: IFileManager, args: MenuEventArgs): void {
    let tick: boolean;
    parent.isSortByClicked = true;
    if (args.item.id.indexOf('ascending') !== -1 || args.item.id.indexOf('descending') !== -1 || args.item.id.indexOf('none') !== -1) {
        tick = true;
    } else {
        tick = false;
    }
    if (!tick) {
        parent.sortBy = getSortField(args.item.id, parent);
    } else {
        parent.sortOrder = <SortOrder>getSortField(args.item.id);
    }
    parent.itemData = [getPathObject(parent)];
    if (parent.view === 'Details') {
        if (parent.isMobile) {
            updateLayout(parent, 'Details');
        } else {
            parent.notify(events.sortColumn, { module: 'detailsview' });
            parent.isSortByClicked = false;
        }
    }
    if (parent.view === 'LargeIcons') {
        updateLayout(parent, 'LargeIcons');
    }

    parent.notify(events.sortByChange, {});
}

/**
 * Gets the sorted fields
 *
 * @param {string} id - specifies the id.
 * @param {IFileManager} [parent] - optional parameter representing the parent IFileManager.
 * @returns {string} - returns the sorted fields
 * @private
 */
export function getSortField(id: string, parent?: IFileManager): string {
    const text: string = id.substring(id.lastIndexOf('_') + 1);
    let field: string = text;
    let column: ColumnModel[];
    if (parent) {
        column = parent.detailsViewSettings.columns;
    }
    switch (text) {
    case 'date':
        for (let i: number = 0, len: number = column.length; i < len; i++) {
            if (column[i as number].field === 'dateModified' || column[i as number].field === 'dateCreated') {
                field = column[i as number].field;
                break;
            }
            else {
                field = '_fm_modified';
            }
        }
        break;
    case 'ascending':
        field = 'Ascending';
        break;
    case 'descending':
        field = 'Descending';
        break;
    case 'none':
        field = 'None';
        break;
    }
    return field;
}

/**
 * Sets the next path
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {string} path - specifies the path.
 * @returns {void}
 * @private
 */
export function setNextPath(parent: IFileManager, path: string): void {
    const currfolders: string[] = path.split('/');
    const folders: string[] = parent.originalPath.split('/');
    const root: Object = getValue(parent.pathId[0], parent.feParent);
    const key: string = isNOU(getValue('id', root)) ? 'name' : 'id';
    for (let i: number = currfolders.length - 1, len: number = folders.length - 1; i < len; i++) {
        const eventName: string = (folders[i + 1] === '') ? events.finalizeEnd : events.initialEnd;
        const newPath: string = (folders[i as number] === '') ? '/' : (parent.path + folders[i as number] + '/');
        const data: Object = getObject(parent, key, folders[parseInt(i.toString(), 10)]);
        const id: string = getValue('_fm_id', data);
        parent.setProperties({ path: newPath }, true);
        if (!isNullOrUndefined(id)) {
            parent.pathId.push(id);
        }
        parent.itemData = [data];
        parent.pathNames.push(getValue('name', data));
        read(parent, eventName, parent.path);
        break;
    }
}

/**
 * Opens the searched folder
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {Object} data - specifies the data
 * @returns {void}
 * @private
 */
export function openSearchFolder(parent: IFileManager, data: Object): void {
    parent.originalPath = getFullPath(parent, data, parent.path);
    const root: Object = getValue(parent.pathId[0], parent.feParent);
    const navData: object = parent.feParent[getValue('_fm_id', parent.itemData[0])];
    const isRoot: boolean = isNullOrUndefined(navData) || getValue('_fm_id', navData) === 'fe_tree';
    const key: string = isNOU(getValue('id', root)) ? 'name' : 'id';
    const searchData: object = getObject(parent, key, isFileSystemData(parent) ? getValue('id', data) : getValue('name', data));
    if (isNullOrUndefined(searchData))
    {
        if (!isRoot)
        {
            parent.notify(events.clearPathInit, { selectedNode: parent.pathId[parent.pathId.length - 1] });
        }
        else
        {
            setNextPath(parent, parent.path);
            return;
        }
    }
    else
    {
        const id: string = getValue('_fm_id', searchData);
        parent.setProperties({ path: parent.originalPath }, true);
        parent.pathId.push(id);
        parent.itemData = [searchData];
        parent.pathNames.push(getValue('name', searchData));
    }
    read(parent, (parent.path !== parent.originalPath) ? events.initialEnd : events.finalizeEnd, parent.path);
}

/**
 * Paste handling function
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @returns {void}
 * @private
 */
export function pasteHandler(parent: IFileManager): void {
    parent.isDragDrop = false;
    if (parent.selectedNodes.length !== 0 && parent.enablePaste) {
        const path: string = (parent.folderPath === '') ? parent.path : parent.folderPath;
        if (parent.activeModule === 'navigationpane' && !parent.selectedNodes[0].includes('/')) {
            parent.targetPath = getTargetPath(parent, parent.actionRecords[0]);
        }
        const subFolder: boolean = validateSubFolder(parent, <{ [key: string]: Object; }[]>parent.actionRecords, path, parent.path);
        if (!subFolder) {
            if ((parent.fileAction === 'move' && parent.targetPath !== path) || parent.fileAction === 'copy') {
                parent.notify(events.pasteInit, {});
                paste(
                    parent, parent.targetPath, parent.selectedNodes, path, parent.fileAction, [], parent.actionRecords);
            } else {
                parent.enablePaste = false;
                parent.notify(events.hidePaste, {});
                removeBlur(parent);
                const result: ReadArgs = {
                    files: null,
                    error: {
                        code: '402',
                        message: getLocaleText(parent, 'Same-Folder-Error'),
                        fileExists: null
                    }
                };
                createDialog(parent, 'Error', result);
            }
        }
    }
}

/**
 * Validates the sub folders
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @param {'{ [key: string]: Object; }[]'} data - specifies the data.
 * @param {string} dropPath - specifies the drop path.
 * @param {string} dragPath - specifies the drag path.
 * @returns {boolean} - returns the validated sub folder.
 * @private
 */
export function validateSubFolder(parent: IFileManager, data: { [key: string]: Object; }[], dropPath: string, dragPath: string): boolean {
    let subFolder: boolean = false;
    for (let i: number = 0; i < data.length; i++) {
        if (!getValue('isFile', data[i as number])) {
            const tempTarget: string = getFullPath(parent, data[i as number], dragPath);
            if (dropPath.indexOf(tempTarget) === 0) {
                const result: ReadArgs = {
                    files: null,
                    error: {
                        code: '402',
                        message: getLocaleText(parent, 'Sub-Folder-Error'),
                        fileExists: null
                    }
                };
                createDialog(parent, 'Error', result);
                subFolder = true;
                break;
            }
        } else {
            const name: Object = parent.dragData[i as number] ? parent.dragData[i as number].name : null;
            const srcData: string = isFileSystemData(parent) ? name as string : parent.dragNodes[i as number];
            let len: number = 0;
            if (srcData) {
                len = srcData.lastIndexOf('/');
            }
            let path: string = '';
            if (len > 0) {
                path = dragPath + srcData.substring(0, len + 1);
            }
            if (path === dropPath) {
                const result: ReadArgs = {
                    files: null,
                    error: {
                        code: '402',
                        message: getLocaleText(parent, 'Same-Folder-Error'),
                        fileExists: null
                    }
                };
                createDialog(parent, 'Error', result);
                subFolder = true;
                break;
            }
        }
    }
    return subFolder;
}

/**
 * Validates the drop handler
 *
 * @param {IFileManager} parent - specifies the parent element.
 * @returns {void}
 * @private
 */
export function dropHandler(parent: IFileManager): void {
    parent.isDragDrop = true;
    if (parent.dragData.length !== 0) {
        parent.dragPath = parent.dragPath.replace(/\\/g, '/');
        parent.dropPath = parent.dropPath.replace(/\\/g, '/');
        const subFolder: boolean = validateSubFolder(parent, parent.dragData, parent.dropPath, parent.dragPath);
        if (!subFolder && (parent.dragPath !== parent.dropPath)) {
            parent.itemData = [parent.dropData];
            paste(
                parent, parent.dragPath, parent.dragNodes, parent.dropPath, 'move', [], parent.dragData);
            parent.notify(events.pasteInit, {});
        }
    }
}

/**
 * Gets the parent path
 *
 * @param {string} oldPath - specifies the old path.
 * @returns {string} - returns the parent path.
 * @private
 */
export function getParentPath(oldPath: string): string {
    const path: string[] = oldPath.split('/');
    let newPath: string = path[0] + '/';
    for (let i: number = 1; i < path.length - 2; i++) {
        newPath += path[i as number] + '/';
    }
    return newPath;
}

/**
 * Gets the directory path
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {ReadArgs} args - returns the read arguements.
 * @returns {string} - returns the directory path
 * @private
 */
export function getDirectoryPath(parent: IFileManager, args: ReadArgs): string {
    const filePath: string = getValue(parent.hasId ? 'id' : 'name', args.cwd) + '/';
    const fPath: string = getValue(parent.hasId && !isNullOrUndefined(parent.ajaxSettings.url) ? 'filterId' : 'filterPath', args.cwd);
    if (!isNOU(fPath)) {
        if (fPath === '') {
            return '/';
        }
        return fPath.replace(/\\/g, '/').replace(/^.*?(?=\/)/, '') + filePath;
    } else {
        return isFileSystemData(parent) ? filePath : parent.path + filePath;
    }
}

/**
 * Gets the do paste path
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {string} operation - specifies the operations.
 * @param {ReadArgs} result - returns the result.
 * @returns {void}
 * @private
 */
export function doPasteUpdate(parent: IFileManager, operation: string, result: ReadArgs): void {
    if (operation === 'move') {
        if (!parent.isDragDrop) {
            parent.enablePaste = false;
            parent.notify(events.hidePaste, {});
            parent.notify(events.cutEnd, result);
        } else {
            parent.notify(events.dragEnd, result);
        }
    }
    if (parent.duplicateItems.length === 0) {
        parent.pasteNodes = [];
    }
    const flag: boolean = false;
    for (let count: number = 0; (count < result.files.length) && !flag; count++) {
        parent.pasteNodes.push(<string>result.files[count as number][parent.hasId ? 'id' : 'name']);
        if (parent.isDragDrop) {
            parent.droppedObjects.push(result.files[count as number]);
        }
    }
    parent.duplicateItems = [];
    parent.duplicateRecords = [];
    if (parent.isDragDrop && !parent.isPasteError) {
        parent.isDropEnd = true;
    } else {
        parent.isDropEnd = false;
    }
    parent.trigger('success', { action: operation, result: result });
    if (!parent.isDragDrop || (parent.path === parent.dragPath) || (parent.path === parent.dropPath)
        || parent.isSearchDrag) {
        parent.isPathDrag = false;
        read(parent, events.pasteEnd, parent.path);
    } else {
        readDropPath(parent);
    }
}

/**
 * Reads the drop path
 *
 * @param {IFileManager} parent - specifies the parent.
 * @returns {void}
 * @private
 */
export function readDropPath(parent: IFileManager): void {
    let pathId: string = getValue('_fm_id', parent.dropData);
    parent.expandedId = pathId;
    parent.itemData = [parent.dropData];
    if (parent.isPathDrag) {
        parent.notify(events.pathDrag, parent.itemData);
    } else {
        if (parent.navigationpaneModule) {
            let node: Element = select('[data-uid="' + pathId + '"]', parent.navigationpaneModule.treeObj.element);
            if (!node) {
                const liElement: Element = document.querySelector('[data-id = "' + getValue('id', parent.dropData) + '"]');
                pathId = liElement.getAttribute('data-uid');
                node = select('[data-uid="' + pathId + '"]', parent.navigationpaneModule.treeObj.element);
            }
            updatePath(<HTMLLIElement>node, parent.dropData, parent);
        }
        read(parent, events.dropPath, parent.dropPath);
    }
}

/**
 * Gets the duplicated path
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {string} name - specifies the name.
 * @returns {object} - returns the duplicated path.
 * @private
 */
export function getDuplicateData(parent: IFileManager, name: string): object {
    let data: object = null;
    const records: object[] = parent.isDragDrop ? parent.dragData : parent.actionRecords;
    for (let i: number = 0; i < records.length; i++) {
        if (getValue('name', records[i as number]) === name) {
            data = records[i as number];
            break;
        }
    }
    return data;
}

/**
 * Gets the create the virtual drag element
 *
 * @param {IFileManager} parent - specifies the parent.
 * @returns {void}
 * @private
 */
export function createVirtualDragElement(parent: IFileManager): void {
    parent.isSearchDrag = false;
    if (parent.breadcrumbbarModule.searchObj.element.value !== '') { parent.isSearchDrag = true; }
    if (parent.activeModule !== 'navigationpane') {
        parent.dragNodes = [];
        let i: number = 0;
        while (i < parent.selectedItems.length) {
            parent.dragNodes.push(parent.selectedItems[i as number]);
            i++;
        }
        if (parent.selectedItems.length === 0 && parent.dragData && parent.dragData.length === 1) {
            parent.dragNodes.push(getItemName(parent, parent.dragData[0]));
        }
    }
    const cloneIcon: HTMLElement = parent.createElement('div', {
        className: 'e-fe-icon ' + fileType(parent.dragData[0])
    });
    const cloneName: HTMLElement = parent.createElement('div', {
        className: 'e-fe-name',
        innerHTML: <string>parent.dragData[0].name
    });
    const virtualEle: HTMLElement = parent.createElement('div', {
        className: 'e-fe-content'
    });
    virtualEle.appendChild(cloneIcon);
    virtualEle.appendChild(cloneName);
    const ele: HTMLElement = parent.createElement('div', {
        className: CLS.CLONE
    });
    ele.appendChild(virtualEle);
    if (parent.dragNodes.length > 1) {
        const badge: HTMLElement = parent.createElement('span', {
            className: 'e-fe-count',
            innerHTML: (parent.dragNodes.length).toString(10)
        });
        ele.appendChild(badge);
    }
    parent.virtualDragElement = ele;
    parent.element.appendChild(parent.virtualDragElement);
}

/**
 * Drops the stop handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {DragEventArgs} args - specifies the drag event arguements.
 * @returns {void}
 * @private
 */
export function dragStopHandler(parent: IFileManager, args: DragEventArgs): void {
    const dragArgs: FileDragEventArgs = args;
    dragArgs.cancel = false;
    if (parent.treeExpandTimer != null) {
        window.clearTimeout(parent.treeExpandTimer);
        parent.treeExpandTimer = null;
    }
    removeDropTarget(parent);
    parent.element.classList.remove('e-fe-drop', 'e-no-drop');
    removeBlur(parent);
    parent.uploadObj.dropArea = <HTMLElement>select('#' + parent.element.id + CLS.CONTENT_ID, parent.element);
    const virtualEle: Element = select('.' + CLS.CLONE, parent.element);
    if (virtualEle) { detach(virtualEle); }
    getTargetModule(parent, args.target);
    parent.notify(events.dropInit, args);
    removeBlur(parent, 'hover');
    dragArgs.fileDetails = parent.dragData;
    parent.trigger('fileDragStop', dragArgs, (dragArgs: FileDragEventArgs) => {
        if (!dragArgs.cancel && !isNOU(parent.targetModule) && parent.targetModule !== '' && parent.dragCount > 2) {
            dropHandler(parent);
        }
        parent.dragCount = 0;
    });
}

/**
 * Drag the start handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {'DragEventArgs'} args - specifies the drag event arguements.
 * @param {Draggable} dragObj - specifies the drag event arguements.
 * @returns {void}
 * @private
 */
export function dragStartHandler(parent: IFileManager, args: DragEventArgs, dragObj: Draggable): void {
    const dragArgs: FileDragEventArgs = args;
    dragArgs.cancel = false;
    dragArgs.fileDetails = parent.dragData;
    parent.dragCount = 0;
    parent.droppedObjects = [];
    if (!parent.allowDragAndDrop || ((parent.activeModule === 'navigationpane') &&
        (closest(args.element, 'li').getAttribute('data-uid') === parent.pathId[0]))) {
        dragArgs.cancel = true;
    }
    if ((parent.activeModule === 'navigationpane') &&
        (parent.pathId.indexOf(closest(args.element, 'li').getAttribute('data-uid')) !== -1)) {
        parent.isPathDrag = true;
    } else {
        parent.isPathDrag = false;
    }
    removeBlur(parent);
    if (dragArgs.cancel) {
        dragObj.intDestroy(args.event);
        dragCancel(parent);
    } else if (!dragArgs.cancel) {
        let i: number = 0;
        while (i < parent.activeElements.length) {
            addBlur(parent.activeElements[i as number]);
            i++;
        }
        parent.trigger('fileDragStart', dragArgs, (dragArgs: FileDragEventArgs) => {
            if (dragArgs.cancel) {
                dragObj.intDestroy(args.event);
                dragCancel(parent);
            } else {
                parent.uploadObj.dropArea = null;
            }
        });
    }
}

/**
 * Drag the cancel handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @returns {void}
 * @private
 */
export function dragCancel(parent: IFileManager): void {
    removeBlur(parent);
    const virtualEle: Element = select('.' + CLS.CLONE, parent.element);
    if (virtualEle) { detach(virtualEle); }
}

/**
 * Remove drop target handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @returns {void}
 * @private
 */
export function removeDropTarget(parent: IFileManager): void {
    removeItemClass(parent, CLS.DROP_FOLDER);
    removeItemClass(parent, CLS.DROP_FILE);
}

/**
 * Remove item class handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {string} value - specifies the value.
 * @returns {void}
 * @private
 */
export function removeItemClass(parent: IFileManager, value: string): void {
    const ele: NodeListOf<Element> = parent.element.querySelectorAll('.' + value);
    for (let i: number = 0; i < ele.length; i++) {
        ele[i as number].classList.remove(value);
    }
}

/**
 * Remove item class handler
 *
 * @param {Element} scrollParent - specifies the scrolling target.
 * @param {IFileManager} parent - specifies the parent.
 * @param {string} nodeClass - specifies the node class.
 * @param {number} clientY - specifies the vertical (Y) coordinate of the mouse cursor position relative to the target element.
 * @returns {void}
 * @private
 */
export function scrollHandler(scrollParent: Element, parent: IFileManager, nodeClass: string, clientY: number): void {
    let position: number;
    const elementData: DOMRect | ClientRect = scrollParent.getBoundingClientRect();
    const node: HTMLElement = select('.' + nodeClass, scrollParent);
    if ((clientY >= (elementData.top + scrollParent.clientHeight - 30)) && !isNullOrUndefined(node)) {
        position = (parent.targetModule === 'navigationpane' || parent.targetModule === 'detailsview') ? node.offsetHeight / 2.5 : node.offsetHeight / 4.5;
        scrollParent.scrollBy(0, position);
    }
    if (!isNullOrUndefined(node) && (clientY <= (elementData.top + 30))) {
        position = (parent.targetModule === 'navigationpane' || parent.targetModule === 'detailsview') ? node.offsetHeight / 2.5 : node.offsetHeight / 4.5;
        scrollParent.scrollBy(0, -position);
    }
}

/**
 * Dragging handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {DragEventArgs} args - specifies the arguements.
 * @returns {void}
 * @private
 */
export function draggingHandler(parent: IFileManager, args: DragEventArgs): void {
    const dragArgs: FileDragEventArgs = args;
    dragArgs.fileDetails = parent.dragData;
    let canDrop: boolean = false;
    getTargetModule(parent, args.target);
    removeDropTarget(parent);
    if (parent.treeExpandTimer != null) {
        window.clearTimeout(parent.treeExpandTimer);
        parent.treeExpandTimer = null;
    }
    removeBlur(parent, 'hover');
    let node: Element = null;
    let scrollParent: Element;
    if (parent.targetModule === 'navigationpane') {
        node = closest(args.target, 'li');
        node.classList.add(CLS.HOVER, CLS.DROP_FOLDER);
        canDrop = true;
        /* istanbul ignore next */
        parent.treeExpandTimer = window.setTimeout(() => { parent.notify(events.dragging, args); }, 800);
        scrollParent = parent.navigationpaneModule.treeObj.element.parentElement;
        scrollHandler(scrollParent, parent, 'e-level-2', args.event.y);
    } else if (parent.targetModule === 'detailsview') {
        node = closest(args.target, 'tr');
        if (node && node.querySelector('.' + CLS.FOLDER) && !node.classList.contains(CLS.BLUR)) {
            node.classList.add(CLS.DROP_FOLDER);
        } else if (node && !node.querySelector('.' + CLS.FOLDER) && !node.classList.contains(CLS.BLUR)) {
            node.classList.add(CLS.DROP_FILE);
        }
        canDrop = true;
        scrollParent = parent.detailsviewModule.gridObj.element.querySelector('.e-content');
        scrollHandler(scrollParent, parent, 'e-row', args.event.y);
    } else if (parent.targetModule === 'largeiconsview') {
        node = closest(args.target, 'li');
        if (node && node.querySelector('.' + CLS.FOLDER) && !node.classList.contains(CLS.BLUR)) {
            node.classList.add(CLS.HOVER, CLS.DROP_FOLDER);
        }
        canDrop = true;
        scrollParent = parent.largeiconsviewModule.element.firstElementChild;
        scrollHandler(scrollParent, parent, 'e-large-icon', args.event.y);
        /* istanbul ignore next */
    } else if (parent.targetModule === 'breadcrumbbar') {
        canDrop = true;
    }
    parent.element.classList.remove('e-fe-drop', 'e-no-drop');
    parent.element.classList.add(canDrop ? 'e-fe-drop' : 'e-no-drop');
    parent.dragCount = parent.dragCount + 1;
    parent.trigger('fileDragging', dragArgs);
}

/**
 * Object to string handler
 *
 * @param {Object} data - specifies the data.
 * @returns {string} returns string converted from Object.
 * @private
 */
// Ignored the message key value in permission object
export function objectToString(data: Object): string {
    let str: string = '';
    const keys: string[] = Object.keys(data);
    for (let i: number = 0; i < keys.length; i++) {
        if (keys[i as number] !== 'message') {
            str += (i === 0 ? '' : ', ') + keys[i as number] + ': ' + getValue(keys[i as number], data);
        }
    }
    return str;
}

/**
 * Get item name handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {Object} data - specifies the data.
 * @returns {string} returns the item name.
 * @private
 */
export function getItemName(parent: IFileManager, data: Object): string {
    if (parent.hasId) {
        return getValue('id', data);
    }
    return getName(parent, data);
}

/**
 * Get item name handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {Object} data - specifies the data.
 * @returns {void}
 * @private
 */
export function updateRenamingData(parent: IFileManager, data: Object): void {
    parent.itemData = [data];
    parent.currentItemText = getValue('name', data);
    parent.isFile = getValue('isFile', data);
    parent.filterPath = getValue('filterPath', data);
}

/**
 * Get item name handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @returns {void}
 * @private
 */
export function doRename(parent: IFileManager): void {
    if (!hasEditAccess(parent.itemData[0])) {
        createDeniedDialog(parent, parent.itemData[0], events.permissionEdit);
    } else {
        createDialog(parent, 'Rename');
    }
}

/* istanbul ignore next */
/**
 * Download handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @returns {void}
 * @private
 */
export function doDownload(parent: IFileManager): void {
    const items: Object[] = parent.itemData;
    for (let i: number = 0; i < items.length; i++) {
        if (!hasDownloadAccess(items[i as number])) {
            createDeniedDialog(parent, items[i as number], events.permissionDownload);
            return;
        }
    }
    if (parent.selectedItems.length > 0) {
        Download(parent, parent.path, parent.selectedItems);
    }
}

/**
 * Delete Files handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {Object[]} data - specifies the data.
 * @param {string[]} newIds - specifies the new Ids.
 * @returns {void}
 * @private
 */
export function doDeleteFiles(parent: IFileManager, data: Object[], newIds: string[]): void {
    for (let i: number = 0; i < data.length; i++) {
        if (!hasEditAccess(data[i as number])) {
            createDeniedDialog(parent, data[i as number], events.permissionEdit);
            return;
        }
    }
    parent.itemData = data;
    Delete(parent, newIds, parent.path, 'delete');
}
/* istanbul ignore next */
/**
 * Download files handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {Object[]} data - specifies the data.
 * @param {string[]} newIds - specifies the new Ids.
 * @returns {void}
 * @private
 */
export function doDownloadFiles(parent: IFileManager, data: Object[], newIds: string[]): void {
    for (let i: number = 0; i < data.length; i++) {
        if (!hasDownloadAccess(data[i as number])) {
            createDeniedDialog(parent, data[i as number], events.permissionDownload);
            return;
        }
    }
    parent.itemData = data;
    if (newIds.length > 0) {
        Download(parent, parent.path, newIds);
    }
}

/**
 * Download files handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {Object} data - specifies the data.
 * @param {string} action - specifies the actions.
 * @returns {void}
 * @private
 */
export function createDeniedDialog(parent: IFileManager, data: Object, action: string): void {
    let message: string = getValue('message', getValue('permission', data));
    if (message === '') {
        message = getLocaleText(parent, 'Access-Message').replace('{0}', getValue('name', data)).replace('{1}', action);
    }
    const response: ReadArgs = {
        error: {
            code: '401',
            fileExists: null,
            message: message
        }
    };
    createDialog(parent, 'Error', response);
}

/**
 * Get Access Classes
 *
 * @param {Object} data - specifies the data.
 * @returns {string} - returns accesses classes.
 * @private
 */
export function getAccessClass(data: Object): string {
    return !hasReadAccess(data) ? 'e-fe-locked e-fe-hidden' : 'e-fe-locked';
}

/**
 * Check read access handler
 *
 * @param {Object} data - specifies the data.
 * @returns {boolean} - returns read access.
 * @private
 */
export function hasReadAccess(data: Object): boolean {
    const permission: Object = getValue('permission', data);
    return (permission && !getValue('read', permission)) ? false : true;
}

/**
 * Check edit access handler
 *
 * @param {Object} data - specifies the data.
 * @returns {boolean} - returns edit access.
 * @private
 */
export function hasEditAccess(data: Object): boolean {
    const permission: Object = getValue('permission', data);
    return permission ? ((getValue('read', permission) && getValue('write', permission))) : true;
}

/**
 * Check content access handler
 *
 * @param {Object} data - specifies the data.
 * @returns {boolean} - returns content access.
 * @private
 */
export function hasContentAccess(data: Object): boolean {
    const permission: Object = getValue('permission', data);
    return permission ? ((getValue('read', permission) && getValue('writeContents', permission))) : true;
}

/**
 * Check upload access handler
 *
 * @param {Object} data - specifies the data.
 * @returns {boolean} - returns upload access.
 * @private
 */
export function hasUploadAccess(data: Object): boolean {
    const permission: Object = getValue('permission', data);
    return permission ? ((getValue('read', permission) && getValue('upload', permission))) : true;
}

/**
 * Check download access handler
 *
 * @param {Object} data - specifies the data.
 * @returns {boolean} - returns download access.
 * @private
 */
export function hasDownloadAccess(data: Object): boolean {
    const permission: Object = getValue('permission', data);
    return permission ? ((getValue('read', permission) && getValue('download', permission))) : true;
}

/**
 * Create new folder handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @returns {void}
 * @private
 */
export function createNewFolder(parent: IFileManager): void {
    const details: Object = parent.itemData[0];
    if (!hasContentAccess(details)) {
        createDeniedDialog(parent, details, events.permissionEditContents);
    } else {
        createDialog(parent, 'NewFolder');
    }
}

/**
 * Upload item handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @returns {void}
 * @private
 */
export function uploadItem(parent: IFileManager): void {
    const details: Object = parent.itemData[0];
    if (!hasUploadAccess(details)) {
        createDeniedDialog(parent, details, events.permissionUpload);
    } else {
        const eleId: string = '#' + parent.element.id + CLS.UPLOAD_ID;
        const uploadEle: HTMLElement = document.querySelector(eleId);
        uploadEle.click();
    }
}

/**
 * Close dialog popup handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @returns {void}
 * @private
 */
export function closePopup(parent: IFileManager): void {
    if (!isNullOrUndefined(parent.dialogObj)) {
        parent.dialogObj.hide();
    }
}

/**
 * Get target path from item data.
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {Object} itemData - specifies the item elements.
 * @returns {string} returns the path.
 * @private
 */
export function getTargetPath(parent: IFileManager, itemData: Object): string {
    if (parent.hasId && !isNOU(getValue('filterId', itemData))) {
        return getValue('filterId', itemData).replace(/\\/g, '/').replace(/^[^/]+\//, '/');
    }
    else {
        return getValue('filterPath', itemData).replace(/\\/g, '/');
    }
}
/**
 * Access control handler
 *
 * @param {IFileManager} parent - specifies the parent.
 * @param {Object[]} itemData - specifies the item elements.
 * @param {string} action - specifies the action.
 * @param {boolean} isPathPermision - specifies the path permission.
 * @returns {string} returns the path.
 * @private
 */
export function getAccessDetails(parent: IFileManager, itemData: Object[], action: string, isPathPermision: boolean): string {
    let accessMessage: string = '';
    for (let i: number = 0; i < itemData.length; i++){
        let permission: object = getValue('permission', itemData[i as number]);
        if (permission == null) {
            permission = undefined;
        }
        if (isPathPermision) {
            if (permission && (!getValue('read', permission) || !getValue('writeContents', permission))) {
                accessMessage = getValue('message', getValue('permission', itemData[i as number]));
                if (accessMessage === '') {
                    accessMessage = getLocaleText(parent, 'Access-Message').replace('{0}', getValue('name', itemData[i as number])).replace('{1}', 'writeContents');
                }
            }
        }
        else {
            const copyOrMovePermission: boolean = action === 'copy' ? getValue('copy', permission) : getValue('write', permission);
            if (permission && (!getValue('read', permission) || !copyOrMovePermission)) {
                accessMessage = getValue('message', getValue('permission', itemData[i as number]));
                if (accessMessage === '') {
                    accessMessage = getLocaleText(parent, 'Access-Message').replace('{0}', getValue('name', itemData[i as number])).replace('{1}', action);
                }
            }
        }
        if (accessMessage !== '') {
            parent.responseData = {
                cwd: null,
                details: null,
                error: {
                    code: '401',
                    message: accessMessage,
                    fileExists: null
                },
                files: null
            };
            break;
        }
    }
    return accessMessage;
}
