/**
     * Create a custom load resource group, note that this method is valid only after the resource configuration file is loaded.
     *
You can monitor the ResourceEvent.CONFIG_COMPLETE event to verify that the configuration is complete.
     * @param name Group name to create the load resource group.
     * @param keys To be included in the list of key keys, the corresponding configuration file in the name or sbuKeys property one or a resource group name.
     * @param override Is the default false for the same name resource group already exists.
     * @returns Create success or fail.
     * @see #setMaxRetryTimes
     * @version Egret 2.4
     * @platform Web,Native
     * @language en_US
     */
/**
     * 创建自定义的加载资源组,注意：此方法仅在资源配置文件加载完成后执行才有效。
     *
可以监听 ResourceEvent.CONFIG_COMPLETE 事件来确认配置加载完成。
     * @param name 要创建的加载资源组的组名。
     * @param keys 要包含的键名列表，key 对应配置文件里的 name 属性或 sbuKeys 属性的一项或一个资源组名。
     * @param override 是否覆盖已经存在的同名资源组,默认 false。
     * @returns 是否创建成功。
     * @see #setMaxRetryTimes
     * @version Egret 2.4
     * @platform Web,Native
     * @language zh_CN
     */
function createGroup(name: string, keys: Array<string>, override?: boolean): boolean;


/**
     * Check whether the configuration file contains the specified resources.
     * @param key A sbuKeys attribute or name property in a configuration file.
     * @see #setMaxRetryTimes
     * @version Egret 2.4
     * @platform Web,Native
     * @language en_US
     */
/**
     * 检查配置文件里是否含有指定的资源。
     * @param key 对应配置文件里的 name 属性或 sbuKeys 属性的一项。
     * @see #setMaxRetryTimes
     * @version Egret 2.4
     * @platform Web,Native
     * @language zh_CN
     */
function hasRes(key: string): boolean;


/**
     * The synchronization method for obtaining the cache has been loaded with the success of the resource.
     *
The type of resource and the corresponding return value types are as follows:
     *
RES.ResourceItem.TYPE_BIN : ArrayBuffer JavaScript primary object
     *
RES.ResourceItem.TYPE_IMAGE : img Html Object，or egret.BitmapData interface。
     *
RES.ResourceItem.TYPE_JSON : Object
     *
RES.ResourceItem.TYPE_SHEET : Object
     *
  1. If the incoming parameter is the name of the entire SpriteSheet is returned is {image1: Texture, "image2": Texture}.
     *
  2. If the incoming is "sheet.image1", the return is a single resource.
     *
  3. If the incoming is the name of the "image1" single resource, the return is a single resource.
     * But if there are two SpriteSheet in a single picture of the same name, the return of the image after the load.
     *
RES.ResourceItem.TYPE_SOUND : HtmlSound Html Object
     *
RES.ResourceItem.TYPE_TEXT : string
     * @param key A subKeys attribute or name property in a configuration file.
     * @see RES.ResourceItem
     * @see #setMaxRetryTimes
     * @version Egret 2.4
     * @platform Web,Native
     * @language en_US
     */
/**
     * 同步方式获取缓存的已经加载成功的资源。
     *
资源类型和对应的返回值类型关系如下：
     *
RES.ResourceItem.TYPE_BIN : ArrayBuffer JavaScript 原生对象
     *
RES.ResourceItem.TYPE_IMAGE : img Html 对象，或者 egret.BitmapData 接口。
     *
RES.ResourceItem.TYPE_JSON : Object
     *
RES.ResourceItem.TYPE_SHEET : Object
     *
  1. 如果传入的参数是整个 SpriteSheet 的名称返回的是 {"image1":Texture,"image2":Texture} 这样的格式。
     *
  2. 如果传入的是 "sheet.image1"，返回的是单个资源。
     *
  3. 如果传入的是 "image1" 单个资源的名称，返回的是单个资源。但是如果有两张 SpriteSheet 中有单个图片资源名称相同，返回的是后加载的那个图片资源。
     *
RES.ResourceItem.TYPE_SOUND : HtmlSound Html 对象
     *
RES.ResourceItem.TYPE_TEXT : string
     * @param key 对应配置文件里的 name 属性或 subKeys 属性的一项。
     * @see RES.ResourceItem
     * @see #setMaxRetryTimes
     * @version Egret 2.4
     * @platform Web,Native
     * @language zh_CN
     */
function getRes(key: string): any;


/**
     * Asynchronous mode to get the resources in the configuration. As long as the resources exist in the configuration file, you can get it in an asynchronous way.
     * @param key A sbuKeys attribute or name property in a configuration file.
     * @param compFunc Call back function. Example：compFunc(data,key):void.
     * @param thisObject This pointer of call back function.
     * @see #setMaxRetryTimes
     * @version Egret 2.4
     * @platform Web,Native
     * @language en_US
     */
/**
     * 异步方式获取配置里的资源。只要是配置文件里存在的资源，都可以通过异步方式获取。
     * @param key 对应配置文件里的 name 属性或 sbuKeys 属性的一项。
     * @param compFunc 回调函数。示例：compFunc(data,key):void。
     * @param thisObject 回调函数的 this 引用。
     * @see #setMaxRetryTimes
     * @version Egret 2.4
     * @platform Web,Native
     * @language zh_CN
     */
function getResAsync(key: string): Promise<any>;


function getResAsync(key: string, compFunc: GetResAsyncCallback, thisObject: any): void;


/**
     * Access to external resources through the full URL.
     * @param url The external path to load the file.
     * @param compFunc Call back function. Example：compFunc(data,url):void。
     * @param thisObject This pointer of call back function.
     * @param type File type (optional). Use the static constants defined in the ResourceItem class. If you do not set the file name extension.
     * @version Egret 2.4
     * @platform Web,Native
     * @includeExample extension/resource/GetResByUrl.ts
     * @language en_US
     */
/**
     * 通过完整URL方式获取外部资源。
     * @param url 要加载文件的外部路径。
     * @param compFunc 回调函数。示例：compFunc(data,url):void。
     * @param thisObject 回调函数的 this 引用。
     * @param type 文件类型(可选)。请使用 ResourceItem 类中定义的静态常量。若不设置将根据文件扩展名生成。
     * @version Egret 2.4
     * @platform Web,Native
     * @includeExample extension/resource/GetResByUrl.ts
     * @language zh_CN
     */
function getResByUrl(url: string, compFunc: Function, thisObject: any, type?: string): void;


/**
     * Destroy a single resource file or a set of resources to the cache data, to return whether to delete success.
     * @param name Name attribute or resource group name of the load item in the configuration file.
     * @param force Destruction of a resource group when the other resources groups have the same resource situation whether the resources will be deleted, the default value true.
     * @returns Are successful destruction.
     * @see #setMaxRetryTimes
     * @version Egret 2.4
     * @platform Web,Native
     * @language en_US
     */
/**
     * 销毁单个资源文件或一组资源的缓存数据,返回是否删除成功。
     * @param name 配置文件中加载项的name属性或资源组名。
     * @param force 销毁一个资源组时其他资源组有同样资源情况资源是否会被删除，默认值 true。
     * @see #setMaxRetryTimes
     * @returns 是否销毁成功。
     * @version Egret 2.4
     * @platform Web,Native
     * @language zh_CN
     */
function destroyRes(name: string, force?: boolean): Promise<boolean>;


/**
     * Sets the maximum number of concurrent load threads, the default value is 2.
     * @param thread The number of concurrent loads to be set.
     * @see #setMaxRetryTimes
     * @version Egret 2.4
     * @platform Web,Native
     * @language en_US
     */
/**
     * 设置最大并发加载线程数量，默认值是 2。
     * @param thread 要设置的并发加载数。
     * @see #setMaxRetryTimes
     * @version Egret 2.4
     * @platform Web,Native
     * @language zh_CN
     */
function setMaxLoadingThread(thread: number): void;


/**
     * Sets the number of retry times when the resource failed to load, and the default value is 3.
     * @param retry To set the retry count.
     * @includeExample extension/resource/Resource.ts
     * @version Egret 2.4
     * @platform Web,Native
     * @language en_US
     */
/**
     * 设置资源加载失败时的重试次数，默认值是 3。
     * @param retry 要设置的重试次数。
     * @includeExample extension/resource/Resource.ts
     * @version Egret 2.4
     * @platform Web,Native
     * @language zh_CN
     */
function setMaxRetryTimes(retry: number): void;


/**
     * Add event listeners, reference ResourceEvent defined constants.
     * @param type Event name。
     * @param listener Listener functions for handling events. This function must accept the Event object as its only parameter, and can't return any results,
     * As shown in the following example: function (evt:Event):void can have any name.
     * @param thisObject The this object that is bound to a function.
     * @param useCapture Determine the listener is running on the capture or running on the target and the bubbling phase. Set useCapture to true,
     * then the listener in the capture phase processing events, but not in the target or the bubbling phase processing events.
     * If useCapture is false, then the listener only in the target or the bubbling phase processing events.
     * To listen for events in all three stages, please call addEventListener two times: once the useCapture is set to true, once the useCapture is set to false.
     * @param priority Event listener priority. Priority is specified by a 32 - bit integer with a symbol. The higher the number, the higher the priority.
     * All listeners with a priority for n will be processed before the -1 n listener.
     * If two or more listeners share the same priority, they are processed in accordance with the order of their added. The default priority is 0.
     * @see RES.ResourceEvent
     * @version Egret 2.4
     * @platform Web,Native
     * @language en_US
     */
/**
     * 添加事件侦听器,参考 ResourceEvent 定义的常量。
     * @param type 事件的类型。
     * @param listener 处理事件的侦听器函数。此函数必须接受 Event 对象作为其唯一的参数，并且不能返回任何结果，
     * 如下面的示例所示： function(evt:Event):void 函数可以有任何名称。
     * @param thisObject 侦听函数绑定的 this 对象。
     * @param useCapture 确定侦听器是运行于捕获阶段还是运行于目标和冒泡阶段。如果将 useCapture 设置为 true，
     * 则侦听器只在捕获阶段处理事件，而不在目标或冒泡阶段处理事件。如果 useCapture 为 false，则侦听器只在目标或冒泡阶段处理事件。
     * 要在所有三个阶段都侦听事件，请调用 addEventListener 两次：一次将 useCapture 设置为 true，一次将 useCapture 设置为 false。
     * @param priority 事件侦听器的优先级。优先级由一个带符号的 32 位整数指定。数字越大，优先级越高。优先级为 n 的所有侦听器会在
     * 优先级为 n -1 的侦听器之前得到处理。如果两个或更多个侦听器共享相同的优先级，则按照它们的添加顺序进行处理。默认优先级为 0。
     * @see RES.ResourceEvent
     * @see #setMaxRetryTimes
     * @version Egret 2.4
     * @platform Web,Native
     * @language zh_CN
     */
function addEventListener(type: string, listener: (event: egret.Event) => void, thisObject: any, useCapture?: boolean, priority?: number): void;


/**
     * Remove event listeners, reference ResourceEvent defined constants.
     * @param type Event name。
     * @param listener Listening function。
     * @param thisObject The this object that is bound to a function.
     * @param useCapture Is used to capture, and this property is only valid in the display list.
     * @version Egret 2.4
     * @platform Web,Native
     * @language en_US
     */
/**
     * 移除事件侦听器,参考ResourceEvent定义的常量。
     * @param type 事件名。
     * @param listener 侦听函数。
     * @param thisObject 侦听函数绑定的this对象。
     * @param useCapture 是否使用捕获，这个属性只在显示列表中生效。
     * @version Egret 2.4
     * @platform Web,Native
     * @language zh_CN
     */
function removeEventListener(type: string, listener: (event: egret.Event) => void, thisObject: any, useCapture?: boolean): void;


/**
     * Adding a custom resource configuration.
     * @param data To add configuration.
     * @version Egret 3.1.6
     * @platform Web,Native
     * @language en_US
     */
/**
     * 自定义添加一项资源配置。
     * @param data 要添加的配置。
     * @version Egret 3.1.6
     * @platform Web,Native
     * @language zh_CN
     */
function $addResourceData(data: {
        name: string;
        type: string;
        url: string;
    }): void;


/**
     * @class RES.ResourceConfig
     * @classdesc
     * @private
     */
class ResourceConfig {
        config: Data;
        constructor();
        init(): Promise<void>;
        __temp__get__type__via__url(url_or_alias: string): string;
        getKeyByAlias(aliasName: string): string;
        /**
         * 创建自定义的加载资源组,注意：此方法仅在资源配置文件加载完成后执行才有效。
         * 可以监听ResourceEvent.CONFIG_COMPLETE事件来确认配置加载完成。
         * @method RES.ResourceConfig#createGroup
         * @param name {string} 要创建的加载资源组的组名
         * @param keys {egret.Array<string>} 要包含的键名列表，key对应配置文件里的name属性或sbuKeys属性的一项或一个资源组名。
         * @param override {boolean} 是否覆盖已经存在的同名资源组,默认false。
         * @returns {boolean}
         */
        createGroup(name: string, keys: Array<string>, override?: boolean): boolean;
        /**
         * 添加一个二级键名到配置列表。
         * @method RES.ResourceConfig#addSubkey
         * @param subkey {string} 要添加的二级键名
         * @param name {string} 二级键名所属的资源name属性
         */
        addSubkey(subkey: string, name: string): void;
        addAlias(alias: any, key: any): void;
        /**
         * 获取加载项类型。
         * @method RES.ResourceConfig#getType
         * @param key {string} 对应配置文件里的name属性或sbuKeys属性的一项。
         * @returns {string}
         */
        getType(key: string): string;
        addResourceData(data: {
            name: string;
            type?: string;
            url: string;
            root?: string;
        }): void;
        destory(): void;
    }


/**
     * @class RES.ResourceLoader
     * @classdesc
     * @private
     */
class ResourceLoader {
        /**
         * 当前组加载的项总个数,key为groupName
         */
        private groupTotalDic;
        /**
         * 已经加载的项个数,key为groupName
         */
        private numLoadedDic;
        /**
         * 正在加载的组列表,key为groupName
         */
        private itemListDic;
        /**
         * 加载失败的组,key为groupName
         */
        private groupErrorDic;
        private retryTimesDic;
        maxRetryTimes: number;
        /**
         * 优先级队列,key为priority，value为groupName列表
         */
        private priorityQueue;
        private reporterDic;
        private dispatcherDic;
        private failedList;
        private loadItemErrorDic;
        private errorDic;
        load(list: ResourceInfo[], groupName: string, priority: number, reporter?: PromiseTaskReporter): Promise<any>;
        private loadingCount;
        thread: number;
        private next();
        /**
         * 从优先级队列中移除指定的组名
         */
        private removeGroupName(groupName);
        private queueIndex;
        /**
         * 获取下一个待加载项
         */
        private getOneResourceInfo();
        loadResource(r: ResourceInfo, p?: RES.processor.Processor): Promise<any>;
        unloadResource(r: ResourceInfo): Promise<any>;
    }


class ResourceManagerError extends Error {
        static errorMessage: {
            1001: string;
            1002: string;
            1005: string;
            2001: string;
            2002: string;
            2003: string;
            2004: string;
            2005: string;
            2006: string;
        };
        /**
         * why instanceof e  != ResourceManagerError ???
         * see link : https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
         */
        private __resource_manager_error__;
        constructor(code: number, replacer?: Object, replacer2?: Object);
    }


class NewFileSystem {
        private data;
        constructor(data: Dictionary);
        profile(): void;
        addFile(filename: string, type?: string): void;
        getFile(filename: string): File | null;
        private reslove(dirpath);
        private mkdir(dirpath);
        private exists(dirpath);
    }


/**
     * The events of resource loading.
     * @version Egret 2.4
     * @platform Web,Native
     * @language en_US
     */
/**
     * 资源加载事件。
     * @version Egret 2.4
     * @platform Web,Native
     * @language zh_CN
     */
class ResourceEvent extends egret.Event {
        /**
         * Failure event for a load item.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 一个加载项加载失败事件。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        static ITEM_LOAD_ERROR: string;
        /**
         * Configure file to load and parse the completion event. Note: if a configuration file is loaded, it will not be thrown out, and if you want to handle the configuration loading failure, monitor the CONFIG_LOAD_ERROR event.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 配置文件加载并解析完成事件。注意：若有配置文件加载失败，将不会抛出此事件，若要处理配置加载失败，请同时监听 CONFIG_LOAD_ERROR 事件。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        static CONFIG_COMPLETE: string;
        /**
         * Configuration file failed to load.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 配置文件加载失败事件。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        static CONFIG_LOAD_ERROR: string;
        /**
         * Delay load group resource loading progress event.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 延迟加载组资源加载进度事件。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        static GROUP_PROGRESS: string;
        /**
         * Delay load group resource to complete event. Note: if you have a resource item loading failure, the event will not be thrown, if you want to handle the group load failure, please listen to the GROUP_LOAD_ERROR event.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 延迟加载组资源加载完成事件。注意：若组内有资源项加载失败，将不会抛出此事件，若要处理组加载失败，请同时监听 GROUP_LOAD_ERROR 事件。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        static GROUP_COMPLETE: string;
        /**
         * Delayed load group resource failed event.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 延迟加载组资源加载失败事件。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        static GROUP_LOAD_ERROR: string;
        /**
         * Creates an Event object to pass as a parameter to event listeners.
         * @param type  The type of the event, accessible as Event.type.
         * @param bubbles  Determines whether the Event object participates in the bubbling stage of the event flow. The default value is false.
         * @param cancelable Determines whether the Event object can be canceled. The default values is false.
         * @version Egret 2.4
         * @platform Web,Native
         * @private
         * @language en_US
         */
        /**
         * 创建一个作为参数传递给事件侦听器的 Event 对象。
         * @param type  事件的类型，可以作为 Event.type 访问。
         * @param bubbles  确定 Event 对象是否参与事件流的冒泡阶段。默认值为 false。
         * @param cancelable 确定是否可以取消 Event 对象。默认值为 false。
         * @version Egret 2.4
         * @platform Web,Native
         * @private
         * @language zh_CN
         */
        constructor(type: string, bubbles?: boolean, cancelable?: boolean);
        /**
         * File number that has been loaded.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 已经加载的文件数。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        itemsLoaded: number;
        /**
         * Total file number to load.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 要加载的总文件数。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        itemsTotal: number;
        /**
         * Resource group name.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 资源组名。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        groupName: string;
        /**
         * An item of information that is finished by the end of a load.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 一次加载项加载结束的项信息对象。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        resItem: ResourceItem;
    }


/**
     * @private
     */
class Resource extends egret.EventDispatcher {
        /**
         * 开始加载配置
         * @method RES.loadConfig
         */
        loadConfig(): Promise<void>;
        /**
         * 检查某个资源组是否已经加载完成
         * @method RES.isGroupLoaded
         * @param name {string}
         */
        isGroupLoaded(name: string): boolean;
        /**
         * 根据组名获取组加载项列表
         * @method RES.getGroupByName
         * @param name {string}
         */
        getGroupByName(name: string): Array<ResourceInfo>;
        /**
         * 根据组名加载一组资源
         * @method RES.loadGroup
         * @param name {string}
         * @param priority {number}
         */
        loadGroup(name: string, priority?: number, reporter?: PromiseTaskReporter): Promise<any>;
        private _loadGroup(name, priority?, reporter?);
        loadResources(keys: string[], reporter?: PromiseTaskReporter): Promise<any>;
        /**
         * 创建自定义的加载资源组,注意：此方法仅在资源配置文件加载完成后执行才有效。
         * 可以监听ResourceEvent.CONFIG_COMPLETE事件来确认配置加载完成。
         * @method RES.ResourceConfig#createGroup
         * @param name {string} 要创建的加载资源组的组名
         * @param keys {egret.Array<string>} 要包含的键名列表，key对应配置文件里的name属性或一个资源组名。
         * @param override {boolean} 是否覆盖已经存在的同名资源组,默认false。
         * @returns {boolean}
         */
        createGroup(name: string, keys: Array<string>, override?: boolean): boolean;
        /**
         * 检查配置文件里是否含有指定的资源
         * @method RES.hasRes
         * @param key {string} 对应配置文件里的name属性或subKeys属性的一项。
         * @returns {boolean}
         */
        hasRes(key: string): boolean;
        /**
         * 通过key同步获取资源
         * @method RES.getRes
         * @param key {string}
         * @returns {any}
         */
        getRes(resKey: string): any;
        /**
         * 通过key异步获取资源
         * @method RES.getResAsync
         * @param key {string}
         * @param compFunc {Function} 回调函数。示例：compFunc(data,url):void。
         * @param thisObject {any}
         */
        getResAsync(key: string): Promise<any>;
        getResAsync(key: string, compFunc: GetResAsyncCallback, thisObject: any): void;
        /**
         * 通过url获取资源
         * @method RES.getResByUrl
         * @param url {string}
         * @param compFunc {Function}
         * @param thisObject {any}
         * @param type {string}
         */
        getResByUrl(url: string, compFunc: Function, thisObject: any, type?: string): Promise<any> | void;
        /**
         * 销毁单个资源文件或一组资源的缓存数据,返回是否删除成功。
         * @method RES.destroyRes
         * @param name {string} 配置文件中加载项的name属性或资源组名
         * @param force {boolean} 销毁一个资源组时其他资源组有同样资源情况资源是否会被删除，默认值true
         * @returns {boolean}
         */
        destroyRes(name: string, force?: boolean): Promise<boolean>;
        /**
         * 设置最大并发加载线程数量，默认值是2.
         * @method RES.setMaxLoadingThread
         * @param thread {number} 要设置的并发加载数。
         */
        setMaxLoadingThread(thread: number): void;
        /**
         * 设置资源加载失败时的重试次数。
         * @param retry 要设置的重试次数。
         */
        setMaxRetryTimes(retry: number): void;
        addResourceData(data: {
            name: string;
            type: string;
            url: string;
        }): void;
    }


interface ResourceInfo {
        url: string;
        type: string;
        root: string;
        crc32?: string;
        size?: number;
        name: string;
        soundType?: string;
        scale9grid?: string;
        groupNames?: string[];
        promise?: Promise<any>;
    }


interface Data {
        resourceRoot: string;
        typeSelector: ResourceTypeSelector;
        mergeSelector: ResourceMergerSelector | null;
        fileSystem: FileSystem;
        groups: {
            [groupName: string]: string[];
        };
        alias: {
            [aliasName: string]: string;
        };
    }


interface ProcessHost {
        state: {
            [index: string]: number;
        };
        resourceConfig: ResourceConfig;
        load: (resource: ResourceInfo, processor?: string | processor.Processor) => Promise<any>;
        unload: (resource: ResourceInfo) => Promise<any>;
        save: (rexource: ResourceInfo, data: any) => void;
        get: (resource: ResourceInfo) => any;
        remove: (resource: ResourceInfo) => void;
    }


/**
     * Promise的回调函数集合
     */
interface PromiseTaskReporter {
        /**
         * 进度回调
         */
        onProgress?: (current: number, total: number) => void;
        /**
         * 取消回调
         */
        onCancel?: () => void;
    }


interface Processor {
        onLoadStart(host: ProcessHost, resource: ResourceInfo): Promise<any>;
        onRemoveStart(host: ProcessHost, resource: ResourceInfo): Promise<any>;
        getData?(host: ProcessHost, resource: ResourceInfo, key: string, subkey: string): any;
    }


interface File {
        url: string;
        type: string;
        name: string;
        root: string;
    }


interface Dictionary {
        [file: string]: File | Dictionary;
    }


interface FileSystem {
        addFile(filename: string, type?: string, root?: string): any;
        getFile(filename: string): File | null;
        profile(): void;
    }


interface ResourceItem extends ResourceInfo {
        /**
         * Name of resource term.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 加载项名称。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        name: string;
        /**
         * URL of resource term.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 要加载的文件地址。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        url: string;
        /**
         * Type of resource term.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 加载项文件类型。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        type: string;
        /**
         * The raw data object to be referenced.
         * @version Egret 2.4
         * @platform Web,Native
         * @language en_US
         */
        /**
         * 被引用的原始数据对象。
         * @version Egret 2.4
         * @platform Web,Native
         * @language zh_CN
         */
        data: ResourceInfo;
        crc32?: string;
        size?: number;
        soundType?: string;
    }


declare type ResourceRootSelector<T extends string> = () => T;


declare type ResourceTypeSelector = (file: string) => string;


declare type ResourceNameSelector = (file: string) => string;


declare type ResourceMergerSelector = (file: string) => {
    path: string;
    alias: string;
};


type GetResAsyncCallback = (value?: any, key?: string) => any;


init(): Promise<void>;


__temp__get__type__via__url(url_or_alias: string): string;


getKeyByAlias(aliasName: string): string;


/**
         * 创建自定义的加载资源组,注意：此方法仅在资源配置文件加载完成后执行才有效。
         * 可以监听ResourceEvent.CONFIG_COMPLETE事件来确认配置加载完成。
         * @method RES.ResourceConfig#createGroup
         * @param name {string} 要创建的加载资源组的组名
         * @param keys {egret.Array<string>} 要包含的键名列表，key对应配置文件里的name属性或sbuKeys属性的一项或一个资源组名。
         * @param override {boolean} 是否覆盖已经存在的同名资源组,默认false。
         * @returns {boolean}
         */
createGroup(name: string, keys: Array<string>, override?: boolean): boolean;


/**
         * 添加一个二级键名到配置列表。
         * @method RES.ResourceConfig#addSubkey
         * @param subkey {string} 要添加的二级键名
         * @param name {string} 二级键名所属的资源name属性
         */
addSubkey(subkey: string, name: string): void;


addAlias(alias: any, key: any): void;


/**
         * 获取加载项类型。
         * @method RES.ResourceConfig#getType
         * @param key {string} 对应配置文件里的name属性或sbuKeys属性的一项。
         * @returns {string}
         */
getType(key: string): string;


addResourceData(data: {
            name: string;
            type?: string;
            url: string;
            root?: string;
        }): void;


destory(): void;


load(list: ResourceInfo[], groupName: string, priority: number, reporter?: PromiseTaskReporter): Promise<any>;


private next();


/**
         * 从优先级队列中移除指定的组名
         */
private removeGroupName(groupName);


/**
         * 获取下一个待加载项
         */
private getOneResourceInfo();


loadResource(r: ResourceInfo, p?: RES.processor.Processor): Promise<any>;


unloadResource(r: ResourceInfo): Promise<any>;


profile(): void;


addFile(filename: string, type?: string): void;


getFile(filename: string): File | null;


private reslove(dirpath);


private mkdir(dirpath);


private exists(dirpath);


/**
         * 开始加载配置
         * @method RES.loadConfig
         */
loadConfig(): Promise<void>;


/**
         * 检查某个资源组是否已经加载完成
         * @method RES.isGroupLoaded
         * @param name {string}
         */
isGroupLoaded(name: string): boolean;


/**
         * 根据组名获取组加载项列表
         * @method RES.getGroupByName
         * @param name {string}
         */
getGroupByName(name: string): Array<ResourceInfo>;


/**
         * 根据组名加载一组资源
         * @method RES.loadGroup
         * @param name {string}
         * @param priority {number}
         */
loadGroup(name: string, priority?: number, reporter?: PromiseTaskReporter): Promise<any>;


private _loadGroup(name, priority?, reporter?);


loadResources(keys: string[], reporter?: PromiseTaskReporter): Promise<any>;


/**
         * 创建自定义的加载资源组,注意：此方法仅在资源配置文件加载完成后执行才有效。
         * 可以监听ResourceEvent.CONFIG_COMPLETE事件来确认配置加载完成。
         * @method RES.ResourceConfig#createGroup
         * @param name {string} 要创建的加载资源组的组名
         * @param keys {egret.Array<string>} 要包含的键名列表，key对应配置文件里的name属性或一个资源组名。
         * @param override {boolean} 是否覆盖已经存在的同名资源组,默认false。
         * @returns {boolean}
         */
createGroup(name: string, keys: Array<string>, override?: boolean): boolean;


/**
         * 检查配置文件里是否含有指定的资源
         * @method RES.hasRes
         * @param key {string} 对应配置文件里的name属性或subKeys属性的一项。
         * @returns {boolean}
         */
hasRes(key: string): boolean;


/**
         * 通过key同步获取资源
         * @method RES.getRes
         * @param key {string}
         * @returns {any}
         */
getRes(resKey: string): any;


/**
         * 通过key异步获取资源
         * @method RES.getResAsync
         * @param key {string}
         * @param compFunc {Function} 回调函数。示例：compFunc(data,url):void。
         * @param thisObject {any}
         */
getResAsync(key: string): Promise<any>;


getResAsync(key: string, compFunc: GetResAsyncCallback, thisObject: any): void;


/**
         * 通过url获取资源
         * @method RES.getResByUrl
         * @param url {string}
         * @param compFunc {Function}
         * @param thisObject {any}
         * @param type {string}
         */
getResByUrl(url: string, compFunc: Function, thisObject: any, type?: string): Promise<any> | void;


/**
         * 销毁单个资源文件或一组资源的缓存数据,返回是否删除成功。
         * @method RES.destroyRes
         * @param name {string} 配置文件中加载项的name属性或资源组名
         * @param force {boolean} 销毁一个资源组时其他资源组有同样资源情况资源是否会被删除，默认值true
         * @returns {boolean}
         */
destroyRes(name: string, force?: boolean): Promise<boolean>;


/**
         * 设置最大并发加载线程数量，默认值是2.
         * @method RES.setMaxLoadingThread
         * @param thread {number} 要设置的并发加载数。
         */
setMaxLoadingThread(thread: number): void;


/**
         * 设置资源加载失败时的重试次数。
         * @param retry 要设置的重试次数。
         */
setMaxRetryTimes(retry: number): void;


addResourceData(data: {
            name: string;
            type: string;
            url: string;
        }): void;


() => 'world'


() => {

  test('isJustObject', () => {
    for (const arr of data) {
      for (const value of arr) {
        expect(isJustObject(value)).toBe(false);
      }
    }
    expect(isJustObject({})).toBe(true);
    expect(isJustObject({hello: 123})).toBe(true);
  });

  test('isNotJustObject', () => {
    for (const arr of data) {
      for (const value of arr) {
        expect(isNotJustObject(value)).toBe(true);
      }
    }
    expect(isNotJustObject({})).toBe(false);
    expect(isNotJustObject({hello: 123})).toBe(false);
  });

}


() => {
    for (const arr of data) {
      for (const value of arr) {
        expect(isJustObject(value)).toBe(false);
      }
    }
    expect(isJustObject({})).toBe(true);
    expect(isJustObject({hello: 123})).toBe(true);
  }


() => {
    for (const arr of data) {
      for (const value of arr) {
        expect(isNotJustObject(value)).toBe(true);
      }
    }
    expect(isNotJustObject({})).toBe(false);
    expect(isNotJustObject({hello: 123})).toBe(false);
  }


(
    baseAudioContextConstructor,
    createInvalidStateError,
    createNotSupportedError,
    createUnknownError,
    mediaElementAudioSourceNodeConstructor,
    mediaStreamAudioDestinationNodeConstructor,
    mediaStreamAudioSourceNodeConstructor,
    mediaStreamTrackAudioSourceNodeConstructor,
    nativeAudioContextConstructor
) => {
    return class AudioContext extends baseAudioContextConstructor<IAudioContext> implements IAudioContext {
        private _baseLatency: number;

        private _nativeAudioContext: TNativeAudioContext;

        private _state: null | 'suspended';

        constructor(options: IAudioContextOptions = {}) {
            if (nativeAudioContextConstructor === null) {
                throw new Error('Missing the native AudioContext constructor.');
            }

            const nativeAudioContext = new nativeAudioContextConstructor(options);

            // Bug #131 Safari returns null when there are four other AudioContexts running already.
            if (nativeAudioContext === null) {
                throw createUnknownError();
            }

            // Bug #51 Only Chrome and Opera throw an error if the given latencyHint is invalid.
            if (!isValidLatencyHint(options.latencyHint)) {
                throw new TypeError(
                    `The provided value '${options.latencyHint}' is not a valid enum value of type AudioContextLatencyCategory.`
                );
            }

            // Bug #150 Only Chrome, Firefox and Opera support setting the sampleRate.
            if (options.sampleRate !== undefined && nativeAudioContext.sampleRate !== options.sampleRate) {
                throw createNotSupportedError();
            }

            super(nativeAudioContext, 2);

            const { latencyHint } = options;
            const { sampleRate } = nativeAudioContext;

            // @todo The values for 'balanced', 'interactive' and 'playback' are just copied from Chrome's implementation.
            this._baseLatency =
                typeof nativeAudioContext.baseLatency === 'number'
                    ? nativeAudioContext.baseLatency
                    : latencyHint === 'balanced'
                    ? 512 / sampleRate
                    : latencyHint === 'interactive' || latencyHint === undefined
                    ? 256 / sampleRate
                    : latencyHint === 'playback'
                    ? 1024 / sampleRate
                    : /*
                       * @todo The min (256) and max (16384) values are taken from the allowed bufferSize values of a
                       * ScriptProcessorNode.
                       */
                      (Math.max(2, Math.min(128, Math.round((latencyHint * sampleRate) / 128))) * 128) / sampleRate;
            this._nativeAudioContext = nativeAudioContext;
            this._state = null;

            /*
             * Bug #34: Chrome and Opera pretend to be running right away, but fire an onstatechange event when the state actually changes
             * to 'running'.
             */
            if (nativeAudioContext.state === 'running') {
                this._state = 'suspended';

                const revokeState = () => {
                    if (this._state === 'suspended') {
                        this._state = null;
                    }

                    nativeAudioContext.removeEventListener('statechange', revokeState);
                };

                nativeAudioContext.addEventListener('statechange', revokeState);
            }
        }

        get baseLatency(): number {
            return this._baseLatency;
        }

        get state(): TAudioContextState {
            return this._state !== null ? this._state : this._nativeAudioContext.state;
        }

        public close(): Promise<void> {
            // Bug #35: Firefox does not throw an error if the AudioContext was closed before.
            if (this.state === 'closed') {
                return this._nativeAudioContext.close().then(() => {
                    throw createInvalidStateError();
                });
            }

            // Bug #34: If the state was set to suspended before it should be revoked now.
            if (this._state === 'suspended') {
                this._state = null;
            }

            return this._nativeAudioContext.close();

            /*
             * Bug #50: Deleting the AudioGraph is currently not possible anymore.
             * ...then(() => deleteAudioGraph(this, this._nativeAudioContext));
             */
        }

        public createMediaElementSource(mediaElement: HTMLMediaElement): IMediaElementAudioSourceNode<this> {
            return new mediaElementAudioSourceNodeConstructor(this, { mediaElement });
        }

        public createMediaStreamDestination(): IMediaStreamAudioDestinationNode<this> {
            return new mediaStreamAudioDestinationNodeConstructor(this);
        }

        public createMediaStreamSource(mediaStream: MediaStream): IMediaStreamAudioSourceNode<this> {
            return new mediaStreamAudioSourceNodeConstructor(this, { mediaStream });
        }

        public createMediaStreamTrackSource(mediaStreamTrack: MediaStreamTrack): IMediaStreamTrackAudioSourceNode<this> {
            return new mediaStreamTrackAudioSourceNodeConstructor(this, { mediaStreamTrack });
        }

        public resume(): Promise<void> {
            if (this._state === 'suspended') {
                return new Promise((resolve, reject) => {
                    const resolvePromise = () => {
                        this._nativeAudioContext.removeEventListener('statechange', resolvePromise);

                        if (this._nativeAudioContext.state === 'running') {
                            resolve();
                        } else {
                            this.resume().then(resolve, reject);
                        }
                    };

                    this._nativeAudioContext.addEventListener('statechange', resolvePromise);
                });
            }

            return this._nativeAudioContext.resume().catch((err) => {
                // Bug #55: Chrome, Edge and Opera do throw an InvalidAccessError instead of an InvalidStateError.
                // Bug #56: Safari invokes the catch handler but without an error.
                if (err === undefined || err.code === 15) {
                    throw createInvalidStateError();
                }

                throw err;
            });
        }

        public suspend(): Promise<void> {
            return this._nativeAudioContext.suspend().catch((err) => {
                // Bug #56: Safari invokes the catch handler but without an error.
                if (err === undefined) {
                    throw createInvalidStateError();
                }

                throw err;
            });
        }
    };
}


() => {
                    if (this._state === 'suspended') {
                        this._state = null;
                    }

                    nativeAudioContext.removeEventListener('statechange', revokeState);
                }


() => {
                    throw createInvalidStateError();
                }


(resolve, reject) => {
                    const resolvePromise = () => {
                        this._nativeAudioContext.removeEventListener('statechange', resolvePromise);

                        if (this._nativeAudioContext.state === 'running') {
                            resolve();
                        } else {
                            this.resume().then(resolve, reject);
                        }
                    };

                    this._nativeAudioContext.addEventListener('statechange', resolvePromise);
                }


() => {
                        this._nativeAudioContext.removeEventListener('statechange', resolvePromise);

                        if (this._nativeAudioContext.state === 'running') {
                            resolve();
                        } else {
                            this.resume().then(resolve, reject);
                        }
                    }


(err) => {
                // Bug #55: Chrome, Edge and Opera do throw an InvalidAccessError instead of an InvalidStateError.
                // Bug #56: Safari invokes the catch handler but without an error.
                if (err === undefined || err.code === 15) {
                    throw createInvalidStateError();
                }

                throw err;
            }


(err) => {
                // Bug #56: Safari invokes the catch handler but without an error.
                if (err === undefined) {
                    throw createInvalidStateError();
                }

                throw err;
            }


public close(): Promise<void> {
            // Bug #35: Firefox does not throw an error if the AudioContext was closed before.
            if (this.state === 'closed') {
                return this._nativeAudioContext.close().then(() => {
                    throw createInvalidStateError();
                });
            }

            // Bug #34: If the state was set to suspended before it should be revoked now.
            if (this._state === 'suspended') {
                this._state = null;
            }

            return this._nativeAudioContext.close();

            /*
             * Bug #50: Deleting the AudioGraph is currently not possible anymore.
             * ...then(() => deleteAudioGraph(this, this._nativeAudioContext));
             */
        }


public createMediaElementSource(mediaElement: HTMLMediaElement): IMediaElementAudioSourceNode<this> {
            return new mediaElementAudioSourceNodeConstructor(this, { mediaElement });
        }


public createMediaStreamDestination(): IMediaStreamAudioDestinationNode<this> {
            return new mediaStreamAudioDestinationNodeConstructor(this);
        }


public createMediaStreamSource(mediaStream: MediaStream): IMediaStreamAudioSourceNode<this> {
            return new mediaStreamAudioSourceNodeConstructor(this, { mediaStream });
        }


public createMediaStreamTrackSource(mediaStreamTrack: MediaStreamTrack): IMediaStreamTrackAudioSourceNode<this> {
            return new mediaStreamTrackAudioSourceNodeConstructor(this, { mediaStreamTrack });
        }


public resume(): Promise<void> {
            if (this._state === 'suspended') {
                return new Promise((resolve, reject) => {
                    const resolvePromise = () => {
                        this._nativeAudioContext.removeEventListener('statechange', resolvePromise);

                        if (this._nativeAudioContext.state === 'running') {
                            resolve();
                        } else {
                            this.resume().then(resolve, reject);
                        }
                    };

                    this._nativeAudioContext.addEventListener('statechange', resolvePromise);
                });
            }

            return this._nativeAudioContext.resume().catch((err) => {
                // Bug #55: Chrome, Edge and Opera do throw an InvalidAccessError instead of an InvalidStateError.
                // Bug #56: Safari invokes the catch handler but without an error.
                if (err === undefined || err.code === 15) {
                    throw createInvalidStateError();
                }

                throw err;
            });
        }


public suspend(): Promise<void> {
            return this._nativeAudioContext.suspend().catch((err) => {
                // Bug #56: Safari invokes the catch handler but without an error.
                if (err === undefined) {
                    throw createInvalidStateError();
                }

                throw err;
            });
        }


function setupHeatmap(component: Heatmap) {
  component.parameters =
      new BehaviorSubject<CollectionParameters|undefined>(mockParameters());
  component.systemTopology = mockTopology();
  component.preview = new BehaviorSubject<Interval|undefined>(undefined);
  component.layers = new BehaviorSubject<Array<BehaviorSubject<Layer>>>([]);
  component.viewport = new BehaviorSubject<Viewport>(new Viewport());
  component.cpuFilter = new BehaviorSubject<string>('');
  component.maxIntervalCount = new BehaviorSubject<number>(5000);
  component.showMigrations = new BehaviorSubject<boolean>(true);
  component.showSleeping = new BehaviorSubject<boolean>(true);
}


function mockParameters(): CollectionParameters {
  const startTime = 1540768090000;
  const endTime = 1540768139000;
  return new CollectionParameters('foo', CPUS, startTime, endTime);
}


function mockTopology(): SystemTopology {
  return new SystemTopology(CPUS);
}


() => {
  beforeEach(waitForAsync(() => {
    document.body.style.width = '500px';
    document.body.style.height = '500px';
    TestBed
        .configureTestingModule({
          imports: [HeatmapModule],
          providers: [
            {provide: 'MetricsService', useClass: LocalMetricsService},
            {provide: 'RenderDataService', useClass: LocalRenderDataService},
            {provide: 'ShortcutService', useClass: ShortcutService},
          ]
        })
        .compileComponents();
  }));

  it('should create', () => {
    const fixture = TestBed.createComponent(Heatmap);
    const component = fixture.componentInstance;
    setupHeatmap(component);
    fixture.detectChanges();
    expect(component).toBeTruthy();
    expect(component.previewGroup).toBeTruthy();
  });

  it('should render preview', () => {
    const fixture = TestBed.createComponent(Heatmap);
    const component = fixture.componentInstance;
    const parameters = mockParameters();
    setupHeatmap(component);
    const cpus = [1, 5, 10];
    component.preview.next(
        new Thread(parameters, 0, cpus, 'foo', 0, 0, 0, 0, 0, 0));
    fixture.detectChanges();
    const element = fixture.nativeElement;
    const previewLines = element.querySelectorAll('.cpu-preview');
    expect(previewLines.length).toEqual(cpus.length);
  });
}


() => {
    document.body.style.width = '500px';
    document.body.style.height = '500px';
    TestBed
        .configureTestingModule({
          imports: [HeatmapModule],
          providers: [
            {provide: 'MetricsService', useClass: LocalMetricsService},
            {provide: 'RenderDataService', useClass: LocalRenderDataService},
            {provide: 'ShortcutService', useClass: ShortcutService},
          ]
        })
        .compileComponents();
  }


() => {
    const fixture = TestBed.createComponent(Heatmap);
    const component = fixture.componentInstance;
    setupHeatmap(component);
    fixture.detectChanges();
    expect(component).toBeTruthy();
    expect(component.previewGroup).toBeTruthy();
  }


() => {
    const fixture = TestBed.createComponent(Heatmap);
    const component = fixture.componentInstance;
    const parameters = mockParameters();
    setupHeatmap(component);
    const cpus = [1, 5, 10];
    component.preview.next(
        new Thread(parameters, 0, cpus, 'foo', 0, 0, 0, 0, 0, 0));
    fixture.detectChanges();
    const element = fixture.nativeElement;
    const previewLines = element.querySelectorAll('.cpu-preview');
    expect(previewLines.length).toEqual(cpus.length);
  }


_type => TypeGraphQL.Float


@TypeGraphQL.ObjectType({
  isAbstract: true
})
export class LectureAvgAggregate {
  @TypeGraphQL.Field(_type => TypeGraphQL.Float, {
    nullable: true
  })
  id!: number | null;

  @TypeGraphQL.Field(_type => TypeGraphQL.Float, {
    nullable: true
  })
  duration!: number | null;

  @TypeGraphQL.Field(_type => TypeGraphQL.Float, {
    nullable: true
  })
  instructorId!: number | null;

  @TypeGraphQL.Field(_type => TypeGraphQL.Float, {
    nullable: true
  })
  subcourseId!: number | null;
}


class BrowserWindowUndoc extends Electron.BrowserWindow {
		browserWindowOptions: Electron.BrowserWindowConstructorOptions
	}


interface NewWindowExtendedEvent extends Electron.NewWindowWebContentsEvent {
		sender: BrowserWindowUndoc
	}


interface ConfigNames {
		openBrowser: string;
		minToTray: string;
		x: string;
		y: string;
		width: string;
		height: string;
		maximized: string;
		hideMenu: string;
	}


interface ConfigDefaults {
		openBrowser: boolean;
		minToTray: boolean;
		x: number;
		y: number;
		width: number;
		height: number;
		maximized: boolean;
		hideMenu: boolean;
	}


interface Config {
		names: ConfigNames;
		defaults: ConfigDefaults;
		store: import("configstore");
	}


type MenuTemplate = Array<(Electron.MenuItemConstructorOptions)>;


type NewWindowEventListener = (event: NewWindowExtendedEvent,
		url: string,
		frameName: string,
		disposition: string,
		options: Electron.BrowserWindowConstructorOptions,
		additionalFeatures: string[],
		referrer: Electron.Referrer,
		postBody: Electron.PostBody) => void;


type NewWindowEventListenerWrapper = (parentWindow: Electron.BrowserWindow) => NewWindowEventListener;


@Component({
    moduleId: module.id,
    selector: 'top-nav',
    templateUrl: 'topnav.html',
})

export class TopNavComponent implements OnInit {
	usuario: Usuario;

	constructor(private router: Router) {}

	    /*Métodos*/
    public ngOnInit(): void {
        this.usuario = JSON.parse(localStorage.getItem('usuario_investimento'));
    }

	changeTheme(color: string): void {
		var link: any = $('<link>');
		link
			.appendTo('head')
			.attr({type : 'text/css', rel : 'stylesheet'})
			.attr('href', 'themes/app-'+color+'.css');
	}

	rtl(): void {
		var body: any = $('body');
		body.toggleClass('rtl');
	}

	sidebarToggler(): void {
		var sidebar: any = $('#sidebar');
		var mainContainer: any = $('.main-container');
		sidebar.toggleClass('sidebar-left-zero');
		mainContainer.toggleClass('main-container-ml-zero');
	}

	logout(): void {
	    localStorage.removeItem('id_token');
	    localStorage.removeItem('usuario_investimento');
    	this.router.navigate(['']);

	}
}


/*Métodos*/
public ngOnInit(): void {
        this.usuario = JSON.parse(localStorage.getItem('usuario_investimento'));
    }


changeTheme(color: string): void {
		var link: any = $('<link>');
		link
			.appendTo('head')
			.attr({type : 'text/css', rel : 'stylesheet'})
			.attr('href', 'themes/app-'+color+'.css');
	}


rtl(): void {
		var body: any = $('body');
		body.toggleClass('rtl');
	}


sidebarToggler(): void {
		var sidebar: any = $('#sidebar');
		var mainContainer: any = $('.main-container');
		sidebar.toggleClass('sidebar-left-zero');
		mainContainer.toggleClass('main-container-ml-zero');
	}


logout(): void {
	    localStorage.removeItem('id_token');
	    localStorage.removeItem('usuario_investimento');
    	this.router.navigate(['']);

	}


export interface Handlers {
    // Form submit handler
    handleFormSubmit: (onSubmit: (values: object) => void) => (e: React.FormEvent<Element>) => void;
    // Form reset handler
    handleFormReset: (onReset?: (e: React.FormEvent<Element>) => void) => (e: React.FormEvent<Element>) => void;

    // Common Focus handler
    handleFocus: (name: string, onFocus: (e: any) => void) => (event: any) => void;
    // Text change handler
    handleTextChange: (name: string) => (event: React.FormEvent<HTMLInputElement>) => void;
    // Number Change handler
    handleNumberChange: (name: string) => (event: React.FormEvent<HTMLInputElement>) => void;
    // RadioGroup change handler
    handleRadioGroupChange: (name: string) => (value: any) => void;
    // SingleSelect change handler
    handleSingleSelectChange: (name: string) => (value: any) => void;
    // DatePicker Component
    handleDateChange: (name: string, displayFormat: DisplayFormat) => (value: any) => void;
    // DateRangePicker Component
    handleDateRangeChange: (name: string, displayFormat: DisplayFormat) => (value: any) => void;
    // CheckboxGroup change handler
    handleCheckboxGroupChange: (name: string) => (values: any[]) => void;
    // MultiSelect change handler
    handleMultiSelectChange: (name: string) => (values: any[]) => void;
    // Checkbox change handler
    handleCheckboxChange: (name: string) => (event: React.FormEvent<HTMLInputElement>) => void;
    // File input Component
    handleFileChange: (name: string, maxSize?: number, onError?: (key: string, message: string) => void) => (files: FileList) => void;
    // Generic Change
    handleChange: (name: string, value: any) => void;
    // Common Focus handler
    getPeriodFromDates: (
        startDate: string | Date,
        endDate: string | Date,
        displayFormat: DisplayFormat
    ) => { startDate: Date; endDate: Date };
}


export interface UseFormResult {
    // Form key
    formKey: number;
    // Form fields values
    values: { [K: string]: any };
    // Form fields handlers
    handlers: Handlers;
    // Form fields error messages
    errorMessages: { [K: string]: any };
    // Function to change error messages
    addErrorMessage: (key: string, message: string) => void;
    // Function can be called to reset the values
    setValues: React.Dispatch<React.SetStateAction<{}>>;
    // Function can be called to reset the error messages
    setErrorMessages: React.Dispatch<React.SetStateAction<{}>>;
}


export function getOverrideExcelButton(bc: IBuilderConfig) {
    const {overrides} = mergeComponents(bc.topbtn, {
        "Override Excel Button": getBtnExcelConfig(bc),
    });

    return overrides["Override Excel Button"];
}


export function getOverrideDragDropButton(bc: IBuilderConfig) {
    const {overrides} = mergeComponents(bc.topbtn, {
        "Override DragDrop Button": getDragDropBtnConfig(bc),
    });

    return overrides["Override DragDrop Button"];
}


export function getOverrideWindowBottomBtn(bc: IBuilderConfig) {
    const {overrides} = mergeComponents(bc.topbtn, {
        "Override Cancel Button": getCancelBtnConfig(bc),
        "Override Save Button": getSaveBtnConfig(bc, 1),
    });

    return [overrides["Override Save Button"], overrides["Override Cancel Button"]];
}


(bc: IBuilderConfig): IBuilderConfig => ({
    [VAR_RECORD_CN_ORDER]: 1e6,
    [VAR_RECORD_DISPLAYED]: "static:f7e324760ede4c88b4f11f0af26c9e97",
    [VAR_RECORD_MASTER_ID]: bc[VAR_RECORD_PAGE_OBJECT_ID],
    [VAR_RECORD_PAGE_OBJECT_ID]: `${bc[VAR_RECORD_PAGE_OBJECT_ID]}-remove`,
    [VAR_RECORD_PARENT_ID]: bc[VAR_RECORD_PAGE_OBJECT_ID],
    confirmquestion: "static:0cd0fc9bff2641f68f0f9712395f7b82",
    handler: "removeSelectedRecordAction",
    iconfont: "trash-o",
    onlyicon: true,
    reqsel: true,
    type: "BTN",
    uitype: "11",
})


(bc: IBuilderConfig): IBuilderConfig => ({
    [VAR_RECORD_CN_ORDER]: 1e6,
    [VAR_RECORD_DISPLAYED]: "static:627518f4034947aa9989507c5688cfff",
    [VAR_RECORD_MASTER_ID]: bc[VAR_RECORD_PAGE_OBJECT_ID],
    [VAR_RECORD_PAGE_OBJECT_ID]: `${bc[VAR_RECORD_PAGE_OBJECT_ID]}-audit`,
    [VAR_RECORD_PARENT_ID]: bc[VAR_RECORD_PAGE_OBJECT_ID],
    iconfont: "info",
    iconfontname: "fa",
    onlyicon: true,
    readonly: false,
    reqsel: true,
    type: "AUDIT_INFO",
    uitype: "11",
})


(bc: IBuilderConfig): IBuilderConfig => ({
    [VAR_RECORD_CN_ORDER]: 1e6,
    [VAR_RECORD_DISPLAYED]: "static:33c9b02a9140428d9747299b9a767abb",
    [VAR_RECORD_MASTER_ID]: bc[VAR_RECORD_PAGE_OBJECT_ID],
    [VAR_RECORD_PAGE_OBJECT_ID]: `${bc[VAR_RECORD_PAGE_OBJECT_ID]}-refresh`,
    [VAR_RECORD_PARENT_ID]: bc[VAR_RECORD_PAGE_OBJECT_ID],
    handler: "onRefresh",
    iconfont: "refresh",
    iconfontname: "fa",
    onlyicon: true,
    readonly: false,
    type: "BTN",
    uitype: "11",
})


(bc: IBuilderConfig): IBuilderConfig => ({
    [VAR_RECORD_CN_ORDER]: 1e6,
    [VAR_RECORD_DISPLAYED]: "static:7578080854a84cc3b4faad62d4499a4b",
    [VAR_RECORD_MASTER_ID]: bc[VAR_RECORD_PAGE_OBJECT_ID],
    [VAR_RECORD_PAGE_OBJECT_ID]: `${bc[VAR_RECORD_PAGE_OBJECT_ID]}-excel`,
    [VAR_RECORD_PARENT_ID]: bc[VAR_RECORD_PAGE_OBJECT_ID],
    ckwindow: "btnexcel",
    disabledemptymaster: true,
    extraplugingate: "PrintJasperServer",
    handler: "onCreateChildWindowMaster",
    iconfont: "table",
    iconfontname: "fa",
    onlyicon: true,
    readonly: false,
    type: "BTN",
    uitype: "11",
})


(bc: IBuilderConfig, layoutTheme: number): IBuilderConfig => ({
    [VAR_RECORD_CN_ORDER]: 1e6,
    [VAR_RECORD_DISPLAYED]: "static:8a930c6b5dd440429c0f0e867ce98316",
    [VAR_RECORD_PAGE_OBJECT_ID]: `${bc[VAR_RECORD_PAGE_OBJECT_ID]}_gridwindow-save`,
    [VAR_RECORD_PARENT_ID]: `${bc[VAR_RECORD_PAGE_OBJECT_ID]}_gridwindow`,
    handler: "onSimpleSaveWindow",
    iconfont: bc.edittype === "inline" && layoutTheme === 2 ? "save" : undefined,
    type: "BTN",
    uitype: "5",
})


(bc: IBuilderConfig, layoutTheme: number): IBuilderConfig => ({
    [VAR_RECORD_CN_ORDER]: 1e6,
    [VAR_RECORD_DISPLAYED]: "static:64aacc431c4c4640b5f2c45def57cae9",
    [VAR_RECORD_PAGE_OBJECT_ID]: `${bc[VAR_RECORD_PAGE_OBJECT_ID]}_gridwindow-cancel`,
    [VAR_RECORD_PARENT_ID]: `${bc[VAR_RECORD_PAGE_OBJECT_ID]}_gridwindow`,
    confirmquestion: "static:9b475e25ae8a40b0b158543b84ba8c08",
    handler: "onCloseWindow",
    iconfont: layoutTheme === 2 ? "times" : undefined,
    readonly: false,
    type: "BTN",
    uitype: "6",
})


(bc: IBuilderConfig): IBuilderConfig => ({
    [VAR_RECORD_CN_ORDER]: 1e6,
    [VAR_RECORD_DISPLAYED]: "static:64aacc431c4c4640b5f2c45def57cae9",
    [VAR_RECORD_PAGE_OBJECT_ID]: `${bc[VAR_RECORD_PAGE_OBJECT_ID]}_gridwindow-cancel`,
    [VAR_RECORD_PARENT_ID]: `${bc[VAR_RECORD_PAGE_OBJECT_ID]}_gridwindow`,
    confirmquestion: "static:9b475e25ae8a40b0b158543b84ba8c08",
    confirmquestionposition: "top",
    handler: "onCloseWindow",
    readonly: false,
    type: "BTN",
    uitype: "6",
})


(bc: IBuilderConfig): IBuilderConfig => ({
    [VAR_RECORD_CN_ORDER]: 1e6,
    [VAR_RECORD_PAGE_OBJECT_ID]: `${bc[VAR_RECORD_PAGE_OBJECT_ID]}_drag-drop`,
    [VAR_RECORD_PARENT_ID]: `${bc[VAR_RECORD_PAGE_OBJECT_ID]}_drag-drop`,
    mode: "2",
    modeaction: "dragdrop",
    type: "BTN",
})


(bc: IBuilderConfig, layoutTheme: number): IGridBtnsConfigType => {
    const {components, overrides} = mergeComponents(bc.topbtn, {
        "Override Audit Button": getBtnAuditConfig(bc),
        "Override Cancel Button":
            bc.edittype === "inline" ? getCancelInlineBtnConfig(bc, layoutTheme) : getCancelBtnConfig(bc),
        "Override Delete Button": getBtnDeleteConfig(bc),
        "Override DragDrop Button": getDragDropBtnConfig(bc),
        "Override Excel Button": getBtnExcelConfig(bc),
        "Override Refresh Button": getBtnRefreshConfig(bc),
        "Override Save Button": getSaveBtnConfig(bc, layoutTheme),
    });
    const btnsCollector: IBuilderConfig[] = [];
    const btns: IBuilderConfig[] = [];

    components.forEach((component: IBuilderConfig) => {
        if (component.type === "BTNCOLLECTOR") {
            btnsCollector.push(component);
        } else {
            btns.push(component);
        }
    });

    return {btns, btnsCollector, overrides};
}


(component: IBuilderConfig) => {
        if (component.type === "BTNCOLLECTOR") {
            btnsCollector.push(component);
        } else {
            btns.push(component);
        }
    }


async () => {
		const all = translateService.getAll();
		should(Object.keys(all)).eql(['de', 'en']);

	}


async () => {
		const text = translateService.translate('invoice');
		should(text).eql('Invoice');
	}


async () => {
		const text = translateService.translate('invoice', null, 'de');
		should(text).eql('Rechnung');
	}


async () => {
		const text1 = translateService.translate('invoice_with_number', {number: 2}, 'de');
		should(text1).eql('Rechnung #2');
	}


async () => {
		checkLocalizedStrings(translateService);
	}


async () => {

		const catalog = translateService.getAll();
		catalog['de'].test = 'monkey translation';

		let errorThrown = false;

		try {
			checkLocalizedStrings(translateService);
		} catch (err) {
			errorThrown = true;
			should(err.code).eql(0);
		}
		delete catalog['de'].test;
		should(errorThrown).eql(true, 'Error wasn\'t thrown');
	}


async () => {
		should(translateService.translate('test1')).eql('Hello \' <> " characters');
		should(translateService.translate('test2', {replace: '<> \' "'})).eql('Hello <> \' "');
	}


async () => {

		const t2 = new TranslateService({
			directory: path.resolve(__dirname, '../locales'),
			defaultLocale: 'en'
		});

		const c1 = translateService.getAll();
		const c2 = t2.getAll();

		should(Object.keys(c1)).eql(['de', 'en']);
		should(Object.keys(c2)).eql(['de', 'en']);

	}


(ms) => new Promise((res) => setTimeout(res, ms))


(res) => setTimeout(res, ms)


(ev) => {
    ev.stopPropagation();
  }


(ev) => {
    this.onChangeEmitter.fire(this.$.value);
  }


async (ev) => {
    if (ev.key === 'Escape') {
      const res: IAsyncResult<boolean>[] = await this.onCancelEmitter.fireAndAwait(this.$.value);
      // 当有回调函数报错或返回结果为false时，终止后续操作
      if (res.some((r) => r.result === false || !!r.err)) {
        return;
      }
      this.destroy();
    }

    if (ev.key === 'Enter') {
      this.isInPendingCommitState = true;
      this.$.disabled = true;
      const res: IAsyncResult<boolean>[] = await this.onCommitEmitter.fireAndAwait(this.$.value);
      // 当有回调函数报错或返回结果为false时，终止后续操作
      if (res.some((r) => r.result === false || !!r.err)) {
        this.isInPendingCommitState = false;
        this.$.disabled = false;
        this.$.focus();
        return;
      }
      this.isInPendingCommitState = false;
      this.$.disabled = false;
      this.destroy();
    }
  }


(r) => r.result === false || !!r.err


() => {
    this.onFocusEmitter.fire(this.$.value);
  }


async (ev) => {
    // 如果Input由于`react-virtualized`被从视图中卸载，在下一帧前Input的isConnected属性不会被更新
    await delay(0);

    // 如果Input组件不在DOM中，但也没有被手动销毁，则表示由于其不在视窗内被`react-virtualized`卸载
    if (!this.$.isConnected && !this.destroyed) {
      return;
    }
    const res: IAsyncResult<boolean>[] = await this.onBlurEmitter.fireAndAwait(this.$.value);
    // 当有回调函数报错或返回结果为false时，终止后续操作
    if (res.some((r) => r.result === false || !!r.err)) {
      return;
    }

    if (!this.isInPendingCommitState) {
      this.destroy();
    }
  }


export interface PromptValidateMessage {
  message: string | void;
  type: PROMPT_VALIDATE_TYPE;
}


export enum PROMPT_VALIDATE_TYPE {
  INFO = 1,
  ERROR = 2,
  WARNING = 3,
}


public focus(): void {
    this.$.focus();
  }


public setSelectionRange(start: number, end: number): void {
    this.$.setSelectionRange(start, end);
  }


public addClassName(classname: string): void {
    this.$.classList.add(classname);
  }


public removeClassName(classname: string): void {
    this.$.classList.remove(classname);
  }


public addAddonAfter(classname: string) {
    if (!this._hasAddonAfter && !this._destroyed) {
      this.$.parentElement?.parentElement?.appendChild(this.$addonAfter);
      this._hasAddonAfter = true;
    }
    const addElement = document.createElement('div');
    addElement.setAttribute('class', classname);
    this.$addonAfter.appendChild(addElement);
  }


public removeAddonAfter() {
    if (this._hasAddonAfter) {
      this.$addonAfter.firstChild?.remove();
      this.$.parentElement?.parentElement?.removeChild(this.$addonAfter);
      this._hasAddonAfter = false;
    }
  }


public addValidateMessage(validateMessage: PromptValidateMessage) {
    if (!this._hasValidateElement && !this._destroyed) {
      // 移动到与input-box同级别
      this.$.parentElement?.parentElement?.parentElement?.appendChild(this.$validate);
      this._hasValidateElement = true;
    }
    let validateBoxClassName = 'validate-message popup ';
    if (validateMessage && validateMessage.type === PROMPT_VALIDATE_TYPE.ERROR) {
      this._validateClassName = 'validate-error';
    } else if (validateMessage && validateMessage.type === PROMPT_VALIDATE_TYPE.WARNING) {
      this._validateClassName = 'validate-warning';
    } else {
      this._validateClassName = 'validate-info';
    }
    validateBoxClassName += this._validateClassName;

    this.$validate.classList.value = validateBoxClassName;
    this.$validate.innerText = validateMessage.message || '';
    this.$.parentElement?.parentElement?.classList.add(this._validateClassName);
  }


public removeValidateMessage() {
    if (this._hasValidateElement) {
      this.$.parentElement?.parentElement?.parentElement?.removeChild(this.$validate);
      this._hasValidateElement = false;
      this.$.parentElement?.parentElement?.classList.remove(this._validateClassName);
    }
  }


public destroy(): void {
    if (this._destroyed) {
      return;
    }
    this._destroyed = true;
    this.$.removeEventListener('click', this.handleClick);
    this.$.removeEventListener('keyup', this.handleKeyup);
    this.$.removeEventListener('keydown', this.handleKeydown);
    this.$.removeEventListener('focus', this.handleFocus);
    this.$.removeEventListener('blur', this.handleBlur);
    this.removeValidateMessage();
    this.removeAddonAfter();
    this.$.disabled = false;
    this.onDestroyEmitter.fire(this.$.value);
    this.disposables.dispose();
  }


(result) => this.formatResult(result)


(attempt) => this.formatAttempt(attempt)


(attempt) => attempt.id


(attempt) => attempt.id == attemptId


(attempt) => this.formatAttemptResults(attempt)


(h5pActivity) => h5pActivity[key] == value


/**
     * Augment CoreEventsData interface with events specific to this service.
     *
     * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
     */
export interface CoreEventsData {
        [AddonModH5PActivitySyncProvider.AUTO_SYNCED]: AddonModH5PActivityAutoSyncData;
    }


/**
 * Basic data for an H5P activity, exported by Moodle class h5pactivity_summary_exporter.
 */
export type AddonModH5PActivityData = {
    id: number; // The primary key of the record.
    course: number; // Course id this h5p activity is part of.
    name: string; // The name of the activity module instance.
    timecreated?: number; // Timestamp of when the instance was added to the course.
    timemodified?: number; // Timestamp of when the instance was last modified.
    intro: string; // H5P activity description.
    introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
    grade?: number; // The maximum grade for submission.
    displayoptions: number; // H5P Button display options.
    enabletracking: number; // Enable xAPI tracking.
    grademethod: number; // Which H5P attempt is used for grading.
    contenthash?: string; // Sha1 hash of file content.
    coursemodule: number; // Coursemodule.
    context: number; // Context ID.
    introfiles: CoreWSExternalFile[];
    package: CoreWSExternalFile[];
    deployedfile?: {
        filename?: string; // File name.
        filepath?: string; // File path.
        filesize?: number; // File size.
        fileurl: string; // Downloadable file url.
        timemodified?: number; // Time modified.
        mimetype?: string; // File mime type.
    };
};


/**
 * Params of mod_h5pactivity_get_h5pactivities_by_courses WS.
 */
export type AddonModH5pactivityGetByCoursesWSParams = {
    courseids?: number[]; // Array of course ids.
};


/**
 * Data returned by mod_h5pactivity_get_h5pactivities_by_courses WS.
 */
export type AddonModH5pactivityGetByCoursesWSResponse = {
    h5pactivities: AddonModH5PActivityData[];
    warnings?: CoreWSExternalWarning[];
};


/**
 * Params of mod_h5pactivity_get_h5pactivity_access_information WS.
 */
export type AddonModH5pactivityGetH5pactivityAccessInformationWSParams = {
    h5pactivityid: number; // H5p activity instance id.
};


/**
 * Data returned by mod_h5pactivity_get_h5pactivity_access_information WS.
 */
export type AddonModH5pactivityGetH5pactivityAccessInformationWSResponse = {
    warnings?: CoreWSExternalWarning[];
    canview?: boolean; // Whether the user has the capability mod/h5pactivity:view allowed.
    canaddinstance?: boolean; // Whether the user has the capability mod/h5pactivity:addinstance allowed.
    cansubmit?: boolean; // Whether the user has the capability mod/h5pactivity:submit allowed.
    canreviewattempts?: boolean; // Whether the user has the capability mod/h5pactivity:reviewattempts allowed.
};


/**
 * Result of WS mod_h5pactivity_get_h5pactivity_access_information.
 */
export type AddonModH5PActivityAccessInfo = AddonModH5pactivityGetH5pactivityAccessInformationWSResponse;


/**
 * Params of mod_h5pactivity_get_attempts WS.
 */
export type AddonModH5pactivityGetAttemptsWSParams = {
    h5pactivityid: number; // H5p activity instance id.
    userids?: number[]; // User ids.
};


/**
 * Data returned by mod_h5pactivity_get_attempts WS.
 */
export type AddonModH5pactivityGetAttemptsWSResponse = {
    activityid: number; // Activity course module ID.
    usersattempts: AddonModH5PActivityWSUserAttempts[]; // The complete users attempts list.
    warnings?: CoreWSExternalWarning[];
};


/**
 * Params of mod_h5pactivity_get_results WS.
 */
export type AddonModH5pactivityGetResultsWSParams = {
    h5pactivityid: number; // H5p activity instance id.
    attemptids?: number[]; // Attempt ids.
};


/**
 * Data returned by mod_h5pactivity_get_results WS.
 */
export type AddonModH5pactivityGetResultsWSResponse = {
    activityid: number; // Activity course module ID.
    attempts: AddonModH5PActivityWSAttemptResults[]; // The complete attempts list.
    warnings?: CoreWSExternalWarning[];
};


/**
 * Attempts results with some calculated data.
 */
export type AddonModH5PActivityAttemptsResults = Omit<AddonModH5pactivityGetResultsWSResponse, 'attempts'> & {
    attempts: AddonModH5PActivityAttemptResults[]; // The complete attempts list.
};


/**
 * Attempts data for a user as returned by the WS mod_h5pactivity_get_attempts.
 */
export type AddonModH5PActivityWSUserAttempts = {
    userid: number; // The user id.
    attempts: AddonModH5PActivityWSAttempt[]; // The complete attempts list.
    scored?: { // Attempts used to grade the activity.
        title: string; // Scored attempts title.
        grademethod: string; // Scored attempts title.
        attempts: AddonModH5PActivityWSAttempt[]; // List of the grading attempts.
    };
};


/**
 * Attempt data as returned by the WS mod_h5pactivity_get_attempts.
 */
export type AddonModH5PActivityWSAttempt = {
    id: number; // ID of the context.
    h5pactivityid: number; // ID of the H5P activity.
    userid: number; // ID of the user.
    timecreated: number; // Attempt creation.
    timemodified: number; // Attempt modified.
    attempt: number; // Attempt number.
    rawscore: number; // Attempt score value.
    maxscore: number; // Attempt max score.
    duration: number; // Attempt duration in seconds.
    completion?: number; // Attempt completion.
    success?: number | null; // Attempt success.
    scaled: number; // Attempt scaled.
};


/**
 * Attempt and results data as returned by the WS mod_h5pactivity_get_results.
 */
export type AddonModH5PActivityWSAttemptResults = AddonModH5PActivityWSAttempt & {
    results?: AddonModH5PActivityWSResult[]; // The results of the attempt.
};


/**
 * Attempt result data as returned by the WS mod_h5pactivity_get_results.
 */
export type AddonModH5PActivityWSResult = {
    id: number; // ID of the context.
    attemptid: number; // ID of the H5P attempt.
    subcontent: string; // Subcontent identifier.
    timecreated: number; // Result creation.
    interactiontype: string; // Interaction type.
    description: string; // Result description.
    content?: string; // Result extra content.
    rawscore: number; // Result score value.
    maxscore: number; // Result max score.
    duration?: number; // Result duration in seconds.
    completion?: number; // Result completion.
    success?: number | null; // Result success.
    optionslabel?: string; // Label used for result options.
    correctlabel?: string; // Label used for correct answers.
    answerlabel?: string; // Label used for user answers.
    track?: boolean; // If the result has valid track information.
    options?: { // The statement options.
        description: string; // Option description.
        id: number; // Option identifier.
        correctanswer: AddonModH5PActivityWSResultAnswer; // The option correct answer.
        useranswer: AddonModH5PActivityWSResultAnswer; // The option user answer.
    }[];
};


/**
 * Result answer as returned by the WS mod_h5pactivity_get_results.
 */
export type AddonModH5PActivityWSResultAnswer = {
    answer?: string; // Option text value.
    correct?: boolean; // If has to be displayed as correct.
    incorrect?: boolean; // If has to be displayed as incorrect.
    text?: boolean; // If has to be displayed as simple text.
    checked?: boolean; // If has to be displayed as a checked option.
    unchecked?: boolean; // If has to be displayed as a unchecked option.
    pass?: boolean; // If has to be displayed as passed.
    fail?: boolean; // If has to be displayed as failed.
};


/**
 * User attempts data with some calculated data.
 */
export type AddonModH5PActivityUserAttempts = Omit<AddonModH5PActivityWSUserAttempts, 'attempts'|'scored'> & {
    attempts: AddonModH5PActivityAttempt[]; // The complete attempts list.
    scored?: { // Attempts used to grade the activity.
        title: string; // Scored attempts title.
        grademethod: string; // Scored attempts title.
        attempts: AddonModH5PActivityAttempt[]; // List of the grading attempts.
    };
};


/**
 * Attempt with some calculated data.
 */
export type AddonModH5PActivityAttempt = AddonModH5PActivityWSAttempt & {
    durationReadable?: string; // Duration in a human readable format.
    durationCompact?: string; // Duration in a "short" human readable format.
};


/**
 * Attempt and results data with some calculated data.
 */
export type AddonModH5PActivityAttemptResults = AddonModH5PActivityAttempt & {
    results?: AddonModH5PActivityWSResult[]; // The results of the attempt.
};


/**
 * Options to pass to getDeployedFile function.
 */
export type AddonModH5PActivityGetDeployedFileOptions = {
    displayOptions?: CoreH5PDisplayOptions; // Display options
    ignoreCache?: boolean; // Whether to ignore cache. Will fail if offline or server down.
    siteId?: string; // Site ID. If not defined, current site.
};


/**
 * Options to pass to getAttemptResults function.
 */
export type AddonModH5PActivityGetAttemptResultsOptions = CoreCourseCommonModWSOptions & {
    userId?: number; // User ID. If not defined, user of the site.
};


/**
 * Options to pass to getAttempts function.
 */
export type AddonModH5PActivityGetAttemptsOptions = AddonModH5PActivityGetAttemptResultsOptions;


/**
 * Params of mod_h5pactivity_view_h5pactivity WS.
 */
export type AddonModH5pactivityViewH5pactivityWSParams = {
    h5pactivityid: number; // H5P activity instance id.
};


/**
     * Format an attempt's data.
     *
     * @param attempt Attempt to format.
     * @return Formatted attempt.
     */
protected formatAttempt(attempt: AddonModH5PActivityWSAttempt): AddonModH5PActivityAttempt {
        const formattedAttempt: AddonModH5PActivityAttempt = attempt;

        formattedAttempt.timecreated = attempt.timecreated * 1000; // Convert to milliseconds.
        formattedAttempt.timemodified = attempt.timemodified * 1000; // Convert to milliseconds.
        formattedAttempt.success = formattedAttempt.success ?? null;

        if (!attempt.duration) {
            formattedAttempt.durationReadable = '-';
            formattedAttempt.durationCompact = '-';
        } else {
            formattedAttempt.durationReadable = CoreTimeUtils.formatTime(attempt.duration);
            formattedAttempt.durationCompact = CoreTimeUtils.formatDurationShort(attempt.duration);
        }

        return formattedAttempt;
    }


/**
     * Format attempt data and results.
     *
     * @param attempt Attempt and results to format.
     */
protected formatAttemptResults(attempt: AddonModH5PActivityWSAttemptResults): AddonModH5PActivityAttemptResults {
        const formattedAttempt: AddonModH5PActivityAttemptResults = this.formatAttempt(attempt);

        formattedAttempt.results = formattedAttempt.results?.map((result) => this.formatResult(result));

        return formattedAttempt;
    }


/**
     * Format the attempts of a user.
     *
     * @param data Data to format.
     * @return Formatted data.
     */
protected formatUserAttempts(data: AddonModH5PActivityWSUserAttempts): AddonModH5PActivityUserAttempts {
        const formatted: AddonModH5PActivityUserAttempts = data;

        formatted.attempts = formatted.attempts.map((attempt) => this.formatAttempt(attempt));

        if (formatted.scored) {
            formatted.scored.attempts = formatted.scored.attempts.map((attempt) => this.formatAttempt(attempt));
        }

        return formatted;
    }


/**
     * Format an attempt's result.
     *
     * @param result Result to format.
     */
protected formatResult(result: AddonModH5PActivityWSResult): AddonModH5PActivityWSResult {
        result.timecreated = result.timecreated * 1000; // Convert to milliseconds.

        return result;
    }


/**
     * Get cache key for access information WS calls.
     *
     * @param id H5P activity ID.
     * @return Cache key.
     */
protected getAccessInformationCacheKey(id: number): string {
        return ROOT_CACHE_KEY + 'accessInfo:' + id;
    }


/**
     * Get access information for a given H5P activity.
     *
     * @param id H5P activity ID.
     * @param options Other options.
     * @return Promise resolved with the data.
     */
async getAccessInformation(id: number, options: CoreCourseCommonModWSOptions = {}): Promise<AddonModH5PActivityAccessInfo> {
        const site = await CoreSites.getSite(options.siteId);

        const params: AddonModH5pactivityGetH5pactivityAccessInformationWSParams = {
            h5pactivityid: id,
        };
        const preSets: CoreSiteWSPreSets = {
            cacheKey: this.getAccessInformationCacheKey(id),
            updateFrequency: CoreSite.FREQUENCY_OFTEN,
            component: AddonModH5PActivityProvider.COMPONENT,
            componentId: options.cmId,
            ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
        };

        return site.read('mod_h5pactivity_get_h5pactivity_access_information', params, preSets);
    }


/**
     * Get attempt results for all user attempts.
     *
     * @param id Activity ID.
     * @param options Other options.
     * @return Promise resolved with the results of the attempt.
     */
async getAllAttemptsResults(
        id: number,
        options?: AddonModH5PActivityGetAttemptResultsOptions,
    ): Promise<AddonModH5PActivityAttemptsResults> {

        const userAttempts = await this.getUserAttempts(id, options);

        const attemptIds = userAttempts.attempts.map((attempt) => attempt.id);

        if (attemptIds.length) {
            // Get all the attempts with a single call.
            return this.getAttemptsResults(id, attemptIds, options);
        } else {
            // No attempts.
            return {
                activityid: id,
                attempts: [],
                warnings: [],
            };
        }
    }


/**
     * Get cache key for results WS calls.
     *
     * @param id Instance ID.
     * @param attemptsIds Attempts IDs.
     * @return Cache key.
     */
protected getAttemptResultsCacheKey(id: number, attemptsIds: number[]): string {
        return this.getAttemptResultsCommonCacheKey(id) + ':' + JSON.stringify(attemptsIds);
    }


/**
     * Get common cache key for results WS calls.
     *
     * @param id Instance ID.
     * @return Cache key.
     */
protected getAttemptResultsCommonCacheKey(id: number): string {
        return ROOT_CACHE_KEY + 'results:' + id;
    }


/**
     * Get attempt results.
     *
     * @param id Activity ID.
     * @param attemptId Attempt ID.
     * @param options Other options.
     * @return Promise resolved with the results of the attempt.
     */
async getAttemptResults(
        id: number,
        attemptId: number,
        options?: AddonModH5PActivityGetAttemptResultsOptions,
    ): Promise<AddonModH5PActivityAttemptResults> {

        options = options || {};

        const site = await CoreSites.getSite(options.siteId);

        const params: AddonModH5pactivityGetResultsWSParams = {
            h5pactivityid: id,
            attemptids: [attemptId],
        };
        const preSets: CoreSiteWSPreSets = {
            cacheKey: this.getAttemptResultsCacheKey(id, params.attemptids!),
            updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
            component: AddonModH5PActivityProvider.COMPONENT,
            componentId: options.cmId,
            ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
        };

        try {
            const response = await site.read<AddonModH5pactivityGetResultsWSResponse>(
                'mod_h5pactivity_get_results',
                params,
                preSets,
            );

            if (response.warnings?.[0]) {
                throw new CoreWSError(response.warnings[0]); // Cannot view attempt.
            }

            return this.formatAttemptResults(response.attempts[0]);
        } catch (error) {
            if (CoreUtils.isWebServiceError(error)) {
                throw error;
            }

            // Check if the full list of results is cached. If so, get the results from there.
            const cacheOptions: AddonModH5PActivityGetAttemptResultsOptions = {
                ...options, // Include all the original options.
                readingStrategy: CoreSitesReadingStrategy.ONLY_CACHE,
            };

            const attemptsResults = await AddonModH5PActivity.getAllAttemptsResults(id, cacheOptions);

            const attempt = attemptsResults.attempts.find((attempt) => attempt.id == attemptId);

            if (!attempt) {
                throw error;
            }

            return attempt;
        }
    }


/**
     * Get attempts results.
     *
     * @param id Activity ID.
     * @param attemptsIds Attempts IDs.
     * @param options Other options.
     * @return Promise resolved with all the attempts.
     */
async getAttemptsResults(
        id: number,
        attemptsIds: number[],
        options?: AddonModH5PActivityGetAttemptResultsOptions,
    ): Promise<AddonModH5PActivityAttemptsResults> {

        options = options || {};

        const site = await CoreSites.getSite(options.siteId);

        const params: AddonModH5pactivityGetResultsWSParams = {
            h5pactivityid: id,
            attemptids: attemptsIds,
        };
        const preSets: CoreSiteWSPreSets = {
            cacheKey: this.getAttemptResultsCommonCacheKey(id),
            updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
            component: AddonModH5PActivityProvider.COMPONENT,
            componentId: options.cmId,
            ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
        };

        const response = await site.read<AddonModH5pactivityGetResultsWSResponse>(
            'mod_h5pactivity_get_results',
            params,
            preSets,
        );

        response.attempts = response.attempts.map((attempt) => this.formatAttemptResults(attempt));

        return response;
    }


/**
     * Get deployed file from an H5P activity instance.
     *
     * @param h5pActivity Activity instance.
     * @param options Options
     * @return Promise resolved with the file.
     */
async getDeployedFile(
        h5pActivity: AddonModH5PActivityData,
        options?: AddonModH5PActivityGetDeployedFileOptions,
    ): Promise<CoreWSFile> {

        if (h5pActivity.deployedfile) {
            // File already deployed and still valid, use this one.
            return h5pActivity.deployedfile;
        }

        if (!h5pActivity.package || !h5pActivity.package[0]) {
            // Shouldn't happen.
            throw new CoreError('No H5P package found.');
        }

        options = options || {};

        // Deploy the file in the server.
        return CoreH5P.getTrustedH5PFile(
            h5pActivity.package[0].fileurl,
            options.displayOptions,
            options.ignoreCache,
            options.siteId,
        );
    }


/**
     * Get cache key for H5P activity data WS calls.
     *
     * @param courseId Course ID.
     * @return Cache key.
     */
protected getH5PActivityDataCacheKey(courseId: number): string {
        return ROOT_CACHE_KEY + 'h5pactivity:' + courseId;
    }


/**
     * Get an H5P activity with key=value. If more than one is found, only the first will be returned.
     *
     * @param courseId Course ID.
     * @param key Name of the property to check.
     * @param value Value to search.
     * @param options Other options.
     * @return Promise resolved with the activity data.
     */
protected async getH5PActivityByField(
        courseId: number,
        key: string,
        value: unknown,
        options: CoreSitesCommonWSOptions = {},
    ): Promise<AddonModH5PActivityData> {

        const site = await CoreSites.getSite(options.siteId);

        const params: AddonModH5pactivityGetByCoursesWSParams = {
            courseids: [courseId],
        };
        const preSets: CoreSiteWSPreSets = {
            cacheKey: this.getH5PActivityDataCacheKey(courseId),
            updateFrequency: CoreSite.FREQUENCY_RARELY,
            component: AddonModH5PActivityProvider.COMPONENT,
            ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
        };

        const response = await site.read<AddonModH5pactivityGetByCoursesWSResponse>(
            'mod_h5pactivity_get_h5pactivities_by_courses',
            params,
            preSets,
        );

        const currentActivity = response.h5pactivities.find((h5pActivity) => h5pActivity[key] == value);

        if (currentActivity) {
            return currentActivity;
        }

        throw new CoreError(Translate.instant('addon.mod_h5pactivity.errorgetactivity'));
    }


/**
     * Get an H5P activity by module ID.
     *
     * @param courseId Course ID.
     * @param cmId Course module ID.
     * @param options Other options.
     * @return Promise resolved with the activity data.
     */
getH5PActivity(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModH5PActivityData> {
        return this.getH5PActivityByField(courseId, 'coursemodule', cmId, options);
    }


/**
     * Get an H5P activity by context ID.
     *
     * @param courseId Course ID.
     * @param contextId Context ID.
     * @param options Other options.
     * @return Promise resolved with the activity data.
     */
getH5PActivityByContextId(
        courseId: number,
        contextId: number,
        options: CoreSitesCommonWSOptions = {},
    ): Promise<AddonModH5PActivityData> {
        return this.getH5PActivityByField(courseId, 'context', contextId, options);
    }


/**
     * Get an H5P activity by instance ID.
     *
     * @param courseId Course ID.
     * @param id Instance ID.
     * @param options Other options.
     * @return Promise resolved with the activity data.
     */
getH5PActivityById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModH5PActivityData> {
        return this.getH5PActivityByField(courseId, 'id', id, options);
    }


/**
     * Get cache key for attemps WS calls.
     *
     * @param id Instance ID.
     * @param userIds User IDs.
     * @return Cache key.
     */
protected getUserAttemptsCacheKey(id: number, userIds: number[]): string {
        return this.getUserAttemptsCommonCacheKey(id) + ':' + JSON.stringify(userIds);
    }


/**
     * Get common cache key for attempts WS calls.
     *
     * @param id Instance ID.
     * @return Cache key.
     */
protected getUserAttemptsCommonCacheKey(id: number): string {
        return ROOT_CACHE_KEY + 'attempts:' + id;
    }


/**
     * Get attempts of a certain user.
     *
     * @param id Activity ID.
     * @param options Other options.
     * @return Promise resolved with the attempts of the user.
     */
async getUserAttempts(
        id: number,
        options: AddonModH5PActivityGetAttemptsOptions = {},
    ): Promise<AddonModH5PActivityUserAttempts> {

        const site = await CoreSites.getSite(options.siteId);

        const params: AddonModH5pactivityGetAttemptsWSParams = {
            h5pactivityid: id,
            userids: [options.userId || site.getUserId()],
        };
        const preSets: CoreSiteWSPreSets = {
            cacheKey: this.getUserAttemptsCacheKey(id, params.userids!),
            updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
            component: AddonModH5PActivityProvider.COMPONENT,
            componentId: options.cmId,
            ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
        };

        const response = await site.read<AddonModH5pactivityGetAttemptsWSResponse>('mod_h5pactivity_get_attempts', params, preSets);

        if (response.warnings?.[0]) {
            throw new CoreWSError(response.warnings[0]); // Cannot view user attempts.
        }

        return this.formatUserAttempts(response.usersattempts[0]);
    }


/**
     * Invalidates access information.
     *
     * @param id H5P activity ID.
     * @param siteId Site ID. If not defined, current site.
     * @return Promise resolved when the data is invalidated.
     */
async invalidateAccessInformation(id: number, siteId?: string): Promise<void> {

        const site = await CoreSites.getSite(siteId);

        await site.invalidateWsCacheForKey(this.getAccessInformationCacheKey(id));
    }


/**
     * Invalidates H5P activity data.
     *
     * @param courseId Course ID.
     * @param siteId Site ID. If not defined, current site.
     * @return Promise resolved when the data is invalidated.
     */
async invalidateActivityData(courseId: number, siteId?: string): Promise<void> {
        const site = await CoreSites.getSite(siteId);

        await site.invalidateWsCacheForKey(this.getH5PActivityDataCacheKey(courseId));
    }


/**
     * Invalidates all attempts results for H5P activity.
     *
     * @param id Activity ID.
     * @param siteId Site ID. If not defined, current site.
     * @return Promise resolved when the data is invalidated.
     */
async invalidateAllResults(id: number, siteId?: string): Promise<void> {
        const site = await CoreSites.getSite(siteId);

        await site.invalidateWsCacheForKey(this.getAttemptResultsCommonCacheKey(id));
    }


/**
     * Invalidates results of a certain attempt for H5P activity.
     *
     * @param id Activity ID.
     * @param attemptId Attempt ID.
     * @param siteId Site ID. If not defined, current site.
     * @return Promise resolved when the data is invalidated.
     */
async invalidateAttemptResults(id: number, attemptId: number, siteId?: string): Promise<void> {
        const site = await CoreSites.getSite(siteId);

        await site.invalidateWsCacheForKey(this.getAttemptResultsCacheKey(id, [attemptId]));
    }


/**
     * Invalidates all users attempts for H5P activity.
     *
     * @param id Activity ID.
     * @param siteId Site ID. If not defined, current site.
     * @return Promise resolved when the data is invalidated.
     */
async invalidateAllUserAttempts(id: number, siteId?: string): Promise<void> {
        const site = await CoreSites.getSite(siteId);

        await site.invalidateWsCacheForKey(this.getUserAttemptsCommonCacheKey(id));
    }


/**
     * Invalidates attempts of a certain user for H5P activity.
     *
     * @param id Activity ID.
     * @param userId User ID. If not defined, current user in the site.
     * @param siteId Site ID. If not defined, current site.
     * @return Promise resolved when the data is invalidated.
     */
async invalidateUserAttempts(id: number, userId?: number, siteId?: string): Promise<void> {
        const site = await CoreSites.getSite(siteId);

        userId = userId || site.getUserId();

        await site.invalidateWsCacheForKey(this.getUserAttemptsCacheKey(id, [userId]));
    }


/**
     * Delete launcher.
     *
     * @return Promise resolved when the launcher file is deleted.
     */
async isPluginEnabled(siteId?: string): Promise<boolean> {
        const site = await CoreSites.getSite(siteId);

        return site.wsAvailable('mod_h5pactivity_get_h5pactivities_by_courses');
    }


/**
     * Report an H5P activity as being viewed.
     *
     * @param id H5P activity ID.
     * @param name Name of the activity.
     * @param siteId Site ID. If not defined, current site.
     * @return Promise resolved when the WS call is successful.
     */
logView(id: number, name?: string, siteId?: string): Promise<void> {
        const params: AddonModH5pactivityViewH5pactivityWSParams = {
            h5pactivityid: id,
        };

        return CoreCourseLogHelper.logSingle(
            'mod_h5pactivity_view_h5pactivity',
            params,
            AddonModH5PActivityProvider.COMPONENT,
            id,
            name,
            'h5pactivity',
            {},
            siteId,
        );
    }


/*
type TypeGuardClass<T, K extends string> = {
  [ P in K]: (arg: any) => arg is T
}
*/
export function assertType<T, C>(
  value: any,
  contextOrFn: [C, TypeGuardFn<T, C>] | TypeGuardFn<T>
): T {
  if (contextOrFn instanceof Function) {
    const hold = contextOrFn(value);
    if (hold === true) {
      return value;
    } else {
      throw new Error(`assert type for ${value} failed`);
    }
  } else {
    const [context, fn] = contextOrFn;
    const hold = fn.call(context, value);
    if (hold === true) {
      return value;
    } else {
      throw new Error(`assert type for ${value} failed`);
    }
  }
}


type TypeGuardFn<T, C = void> = (this: C, arg: any) => arg is T;


() => {
  let service: ServicioVisualizarService;

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(ServicioVisualizarService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });
}


() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(ServicioVisualizarService);
  }


(success) =>{
        this.gituser =this.serviceusersearch.user;
      }


(error)=>{
        alert(error);
      }


(success)=>{
        this.newrepo = success
        return(this.newrepo)
      }


@Component({
  selector: 'app-user-data',
  templateUrl: './user-data.component.html',
  styleUrls: ['./user-data.component.css']
})
export class UserDataComponent implements OnInit {
  gituser!: UserData[];
  newrepo!: Repos[];

  constructor(public serviceusersearch: UserSearchService, public reposearch: RepoSearchService) { }

  userinput(search: any) {
    this.serviceusersearch.usersearch(search).then(
      (success) =>{
        this.gituser =this.serviceusersearch.user;
      },
      (error)=>{
        alert(error);
      }
    );
    this.reposearch.FetchRepositories(search).subscribe(
      (success)=>{
        this.newrepo = success
        return(this.newrepo)
      }
    )

  }

  ngOnInit(): void {
    this.userinput('antonymburia');
  }

}


userinput(search: any) {
    this.serviceusersearch.usersearch(search).then(
      (success) =>{
        this.gituser =this.serviceusersearch.user;
      },
      (error)=>{
        alert(error);
      }
    );
    this.reposearch.FetchRepositories(search).subscribe(
      (success)=>{
        this.newrepo = success
        return(this.newrepo)
      }
    )

  }


ngOnInit(): void {
    this.userinput('antonymburia');
  }


export interface DeleteLinkResponse {
  /**
   * Unique Oracle-assigned identifier for the request. If you need to contact
   * Oracle about a particular request, please provide the request ID.
   *
   */
  "opcRequestId": string;
}


export class PlatformWrapper extends EventEmitter {
    private platform: any = null

    /**
     * Here we set an adprovider, any can be given as long as it implements the IProvider interface
     *
     * @param platform
     */
    public setPlatform(platform: any): void {
        if (platform) {
            this.platform = platform
            this.platform.setWrapper(this)
        }
    }

    public gameLoaded(...args: any[]): void {
        if (null === this.platform) {
            //Silently return for when no platform is set
            return
        }

        this.platform.gameLoaded.apply(this.platform, args)
    }

    public gameStarted(...args: any[]): void {
        if (null === this.platform) {
            //Silently return for when no platform is set
            return
        }

        this.platform.gameStarted.apply(this.platform, args)
    }

    public getGameSettings(...args: any[]): any {
        if (null === this.platform) {
            //Silently return for when no platform is set
            return
        }

        this.platform.getGameSettings.apply(this.platform, args)
    }

    public gameEnded(fail: boolean, score?: number, level?: number | string, ...args: any[]): void {
        if (null === this.platform) {
            throw new Error(
                'Can not request an ad without an provider, please attach an ad provider!'
            )
        }

        this.platform.gameEnded.apply(this.platform, args)

        if (score) {
            this.sendScore(score)
        }
    }

    public gamePaused(...args: any[]): void {
        if (null === this.platform) {
            //Silently return for when no platform is set
            return
        }

        this.platform.gamePaused.apply(this.platform, args)
    }

    public gameResumed(...args: any[]): void {
        if (null === this.platform) {
            //Silently return for when no platform is set
            return
        }

        this.platform.gameResumed.apply(this.platform, args)
    }

    public sendScore(score: number): void {
        if (null === this.platform) {
            //Silently return for when no platform is set
            return
        }

        this.platform.sendScore.apply(this.platform, score)
    }
}


/**
     * Here we set an adprovider, any can be given as long as it implements the IProvider interface
     *
     * @param platform
     */
public setPlatform(platform: any): void {
        if (platform) {
            this.platform = platform
            this.platform.setWrapper(this)
        }
    }


public gameLoaded(...args: any[]): void {
        if (null === this.platform) {
            //Silently return for when no platform is set
            return
        }

        this.platform.gameLoaded.apply(this.platform, args)
    }