| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827 | /*    ArOZ Online Module Javascript Wrapper    This is a wrapper for module developers to access system API easier and need not to dig through the source code.    Basically: Write less do more (?)    WARNING! SOME FUNCTION ARE NOT COMPATIBILE WITH PREVIOUS VERSION OF AO_MODULE.JS.    PLEASE REFER TO THE SYSTEM DOCUMENTATION FOR MORE INFORMATION.    *** Please include this javascript file with relative path instead of absolute path.    E.g. ../script/ao_module.js (OK)           /script/ao_module.js (NOT OK)*/var ao_module_virtualDesktop = !(!parent.isDesktopMode);var ao_root = null;//Get the current windowID if in Virtual Desktop Mode, return false if VDI is not detectedvar ao_module_windowID = false;var ao_module_parentID = false;var ao_module_callback = false;if (ao_module_virtualDesktop)ao_module_windowID = $(window.frameElement).parent().parent().attr("windowId");if (ao_module_virtualDesktop)ao_module_parentID = $(window.frameElement).parent().parent().attr("parent");if (ao_module_virtualDesktop)ao_module_callback = $(window.frameElement).parent().parent().attr("callback");if (ao_module_virtualDesktop)ao_module_parentURL = $(window.frameElement).parent().find("iframe").attr("src");ao_root = ao_module_getAORootFromScriptPath();/*    Event bindings    The following events are required for ao_module to operate normally     under Web Desktop Mode. */document.addEventListener("DOMContentLoaded", function() {    if (ao_module_virtualDesktop){        //Add window focus handler        document.addEventListener("mousedown", function(event) {            //When click on this document, focus this            ao_module_focus();            if (event.target.tagName == "INPUT" || event.target.tagName == "TEXTAREA"){            }else{                if (parent.window.ime.focus != null){                    parent.window.ime.focus = null;                }            }        }, true);            //Add IME registration handler        var inputFields = document.querySelectorAll("input,textarea");        for (var i = 0; i < inputFields.length; i++){            if ($(inputFields[i]).attr("type") != undefined){                var thisType = $(inputFields[i]).attr("type");                if ((thisType == "text" || thisType =="search" || thisType =="url")){                    //Supported types of input                    ao_module_bindCustomIMEEvents(inputFields[i]);                }else{                    //Not supported type of inputs                }            }else{                //text area                ao_module_bindCustomIMEEvents(inputFields[i]);            }        }    }});/*    Startup Section Script        These functions handle the startup of an ao_module and adapt them into the    standard arozos desktop eco-system api*///Function handle to bind custom IME eventsfunction ao_module_bindCustomIMEEvents(object){    parent.bindObjectToIMEEvents(object);}//Get the ao_root from script includsion pathfunction ao_module_getAORootFromScriptPath(){    var possibleRoot = "";    $("script").each(function(){        if (this.hasAttribute("src") && $(this).attr("src").includes("ao_module.js")){            var tmp = $(this).attr("src");            tmp = tmp.split("script/ao_module.js");            possibleRoot = tmp[0];        }    });    return possibleRoot;}//Get the input filename and filepath from the window hash paramterfunction ao_module_loadInputFiles(){    try{        if (window.location.hash.length == 0){            return null;        }        var inputFileInfo = window.location.hash.substring(1,window.location.hash.length);        inputFileInfo = JSON.parse(decodeURIComponent(inputFileInfo));        return inputFileInfo    }catch{        return null;    }}//Set the ao_module window to fixed size (not allowing resize)function ao_module_setFixedWindowSize(){    if (!ao_module_virtualDesktop){        return;    }    parent.setFloatWindowResizePolicy(ao_module_windowID, false);}//Restore a float window to be resizblefunction ao_module_setResizableWindowSize(){    if (!ao_module_virtualDesktop){        return;    }    parent.setFloatWindowResizePolicy(ao_module_windowID, true);}//Update the window size of the given float window objectfunction ao_module_setWindowSize(width, height){    if (!ao_module_virtualDesktop){        return;    }    parent.setFloatWindowSize(ao_module_windowID, width, height)}//Update the floatWindow titlefunction ao_module_setWindowTitle(newTitle){    if (!ao_module_virtualDesktop){        document.title = newTitle;        return;    }    parent.setFloatWindowTitle(ao_module_windowID, newTitle);}   //Set new window theme, default dark, support {dark/white}function ao_module_setWindowTheme(newtheme="dark"){    if (!ao_module_virtualDesktop){        return;    }    parent.setFloatWindowTheme(ao_module_windowID, newtheme);}   //Check if there are any windows with the same path. //If yes, replace its hash content and reload to the new one and clise the current floatWindowfunction ao_module_makeSingleInstance(){    $(window.parent.document).find(".floatWindow").each(function(){        if ($(this).attr("windowid") == ao_module_windowID){            return        }        var currentPath = window.location.pathname;        if ("/" + $(this).find("iframe").attr('src').split("#").shift() == currentPath){            //Another instance already running. Replace it with the current path            $(this).find("iframe").attr('src', window.location.pathname.substring(1) + window.location.hash);            $(this).find("iframe")[0].contentWindow.location.reload();            //Move the other instant to top            var targetfw = parent.getFloatWindowByID($(this).attr("windowid"))            parent.MoveFloatWindowToTop(targetfw);            //Close the instance            ao_module_close();            return true        }    });    return false}//Close the current windowfunction ao_module_close(){    if (!ao_module_virtualDesktop){        window.close('','_parent','');        window.location.href = ao_root + "SystemAO/closeTabInsturction.html";        return;    }    parent.closeFwProcess(ao_module_windowID);}//Focus this floatWindowfunction ao_module_focus(){    parent.MoveFloatWindowToTop(parent.getFloatWindowByID(ao_module_windowID));}//Set the floatWindow to top most modefunction ao_module_setTopMost(){    parent.PinFloatWindowToTopMostMode(parent.getFloatWindowByID(ao_module_windowID));}//Unset the floatWindow top most modefunction ao_module_unsetTopMost(){    parent.UnpinFloatWindowFromTopMostMode(parent.getFloatWindowByID(ao_module_windowID));}//Popup a file selection window for uplaodfunction ao_module_selectFiles(callback, fileType="file", accept="*", allowMultiple=false){    var input = document.createElement('input');    input.type = fileType;    input.multiple = allowMultiple;    input.accept = accept;    input.onchange = e => {         var files = e.target.files;         callback(files);    }    input.click();}//Open a path with File Manager, optional highligh filenamefunction ao_module_openPath(path, filename=undefined){    //Trim away the last / if exists    if (path.substr(path.length - 1, 1) == "/"){        path = path.substr(0, path.length - 1);    }    if (filename == undefined){        if (ao_module_virtualDesktop){            parent.newFloatWindow({                url: "SystemAO/file_system/file_explorer.html#" + encodeURIComponent(path),                appicon: "SystemAO/file_system/img/small_icon.png",                width:1080,                height:580,                title: "File Manager"            });        }else{            window.open(ao_root + "SystemAO/file_system/file_explorer.html#" + encodeURIComponent(path))        }    }else{        var fileObject = [{            filepath: path + "/" + filename,            filename: filename,        }];        if (ao_module_virtualDesktop){            parent.newFloatWindow({                url: "SystemAO/file_system/file_explorer.html#" + encodeURIComponent(JSON.stringify(fileObject)),                appicon: "SystemAO/file_system/img/small_icon.png",                width:1080,                height:580,                title: "File Manager"            });        }else{            window.open(ao_root + "SystemAO/file_system/file_explorer.html#" + encodeURIComponent(JSON.stringify(fileObject)))        }    }   }//Open a particular tab using System Setting module. Require //1) Setting Group//2) Setting Namefunction ao_module_openSetting(group, name){    var requestObject = {        group: group,        name: name    }    requestObject = encodeURIComponent(JSON.stringify(requestObject));    var openURL = "SystemAO/system_setting/index.html#" + requestObject;    if (ao_module_virtualDesktop){        ao_module_newfw({            url: openURL,            width: 1080,            height: 580,            appicon: "SystemAO/system_setting/img/small_icon.png",            title: "System Setting"        });    }else{        window.open(ao_root + openURL)    }}/*    ao_module_newfw(launchConfig) => Create a new floatWindow object from the given paramters    Most basic usage: (With auto assign UID, size and location)    ao_module_newfw({        url: "Dummy/index.html",        title: "Dummy Module",        appicon: "Dummy/img/icon.png"    });    Example usage that involve all configs:    ao_module_newfw({        url: "Dummy/index.html",        uid: "CustomUUID",        width: 1024,        height: 768,        appicon: "Dummy/img/icon.png",        title: "Dummy Module",        left: 100,        top: 100,        parent: ao_module_windowID,        callback: "childCallbackHandler"    });*/function ao_module_newfw(launchConfig){    if (launchConfig["parent"] == undefined){        launchConfig["parent"] = ao_module_windowID;    }    if (ao_module_virtualDesktop){        parent.newFloatWindow(launchConfig);    }else{        window.open(ao_root + launchConfig.url);    }}/*    File Selector    Open a file selector and return selected item back to the current window    Tips: Unlike the beta version, you can use this function in both Virtual Desktop Mode and normal mode.    Possible selection type:    type => {file / folder / all / new}    Example usage:     ao_module_openFileSelector(fileSelected, "user:/Desktop", "file",true);    function fileSelected(filedata){        for (var i=0; i < filedata.length; i++){            var filename = filedata[i].filename;            var filepath = filedata[i].filepath;            //Do something here        }    }    If you want to create a new file or folder object, you can use the following options paramters    option = {        defaultName: "newfile.txt",            //Default filename used in new operation        fnameOverride: "myfunction",           //For those defined with window.myfunction        filter: ["mp3","aac","ogg","flac","wav"] //File extension filter    }*/let ao_module_fileSelectionListener;let ao_module_fileSelectorWindow;function ao_module_openFileSelector(callback,root="user:/", type="file",allowMultiple=false, options=undefined){    var initInfo = {        root: root,        type: type,        allowMultiple: allowMultiple,        listenerUUID: "",        options: options    }    var initInfoEncoded = encodeURIComponent(JSON.stringify(initInfo))    if (ao_module_virtualDesktop){        var callbackname = callback.name;        if (typeof(options) != "undefined" && typeof(options.fnameOverride) != "undefined"){            callbackname = options.fnameOverride;        }        console.log(callbackname);        parent.newFloatWindow({            url: "SystemAO/file_system/file_selector.html#" + initInfoEncoded,            width: 700,            height: 440,            appicon: "SystemAO/file_system/img/selector.png",            title: "Open",            parent: ao_module_windowID,            callback: callbackname        });    }else{        //Create a return listener base on localStorage        let listenerUUID = "fileSelector_" + new Date().getTime();        ao_module_fileSelectionListener = setInterval(function(){            if (localStorage.getItem(listenerUUID) === undefined || localStorage.getItem(listenerUUID)=== null){                //Not ready            }else{                //File ready!                var selectedFiles = JSON.parse(localStorage.getItem(listenerUUID));                console.log("Removing Localstorage Item " + listenerUUID);                                localStorage.removeItem(listenerUUID);                 setTimeout(function(){                    localStorage.removeItem(listenerUUID);                 },500);                if(selectedFiles == "&&selection_canceled&&"){                    //Selection canceled. Returm empty array                    callback([]);                }else{                    //Files Selected                    callback(selectedFiles);                }                                clearInterval(ao_module_fileSelectionListener);                ao_module_fileSelectorWindow.close();            }        },1000);        //Open the file selector in a new tab        initInfo.listenerUUID = listenerUUID;        initInfoEncoded = encodeURIComponent(JSON.stringify(initInfo))        ao_module_fileSelectorWindow = window.open(ao_root + "SystemAO/file_system/file_selector.html#" + initInfoEncoded,);    }}//Check if there is parent to callbackfunction ao_module_hasParentCallback(){    if (ao_module_virtualDesktop){        //Check if parent callback exists        var thisFw;        $(parent.window.document.body).find(".floatWindow").each(function(){            if ($(this).attr('windowid') == ao_module_windowID){                thisFw = $(this);            }        });        var parentWindowID = thisFw.attr("parent");        var parentCallback = thisFw.attr("callback");        if (parentWindowID == "" || parentCallback == ""){            //No parent window defined            return false;        }        //Check if parent windows is alive        var parentWindow = undefined;        $(parent.window.document.body).find(".floatWindow").each(function(){            if ($(this).attr('windowid') == parentWindowID){                parentWindow = $(this);            }        });        if (parentWindow == undefined){            //parent window not exists            return false;        }        //Parent callback is set and ready to callback        return true;    }else{        return false    }}//Callback to parent with resultsfunction ao_module_parentCallback(data=""){    if (ao_module_virtualDesktop){        var thisFw;        $(parent.window.document.body).find(".floatWindow").each(function(){            if ($(this).attr('windowid') == ao_module_windowID){                thisFw = $(this);            }        });        var parentWindowID = thisFw.attr("parent");        var parentCallback = thisFw.attr("callback");        if (parentWindowID == "" || parentCallback == ""){            //No parent window defined            console.log("Undefined parent window ID or callback name");            return false;        }        var parentWindow = undefined;        $(parent.window.document.body).find(".floatWindow").each(function(){            if ($(this).attr('windowid') == parentWindowID){                parentWindow = $(this);            }        });        if (parentWindow == undefined){            //parent window not exists            console.log("Parent Window not exists!")            return false;        }        $(parentWindow).find('iframe')[0].contentWindow.eval(parentCallback + "(" + JSON.stringify(data) + ");")        //Focus the parent windows        parent.MoveFloatWindowToTop(parentWindow);        return true;    }else{        console.log("[ao_module] WARNING! Invalid call to parentCallback under non-virtualDesktop mode");        return false;    }}function ao_module_agirun(scriptpath, data, callback, failedcallback = undefined, timeout=0){    $.ajax({        url: ao_root + "system/ajgi/interface?script=" + scriptpath,        method: "POST",        data: data,        success: function(data){            if (typeof(callback) != "undefined"){                callback(data);            }        },        error: function(){            if (typeof failedcallback != "undefined"){                failedcallback();            }        },        timeout: timeout    });}function ao_module_uploadFile(file, targetPath, callback=undefined, progressCallback=undefined, failedcallback=undefined) {    let url = ao_root + 'system/file_system/upload'    let formData = new FormData()    let xhr = new XMLHttpRequest()    formData.append('file', file);    formData.append('path', targetPath);    xhr.open('POST', url, true);    xhr.upload.addEventListener("progress", function(e) {        if (progressCallback !== undefined){            progressCallback((e.loaded * 100.0 / e.total) || 100);        }    });    xhr.addEventListener('readystatechange', function(e) {        if (xhr.readyState == 4 && xhr.status == 200) {            if (callback !== undefined){                callback(e.target.response);            }        }        else if (xhr.readyState == 4 && xhr.status != 200) {            if (failedcallback !== undefined){                failedcallback(xhr.status);            }        }    })    xhr.send(formData);}/*    ao_module_storage, allow key-value storage per module settings.     WARNING: NOT CROSS USER READ-WRITABLE        ao_module_storage.setStorage(moduleName, configName,configValue);    ao_module_storage.loadStorage(moduleName, configName);*/class ao_module_storage {    static setStorage(moduleName, configName,configValue){    	$.ajax({    	  type: 'GET',    	  url: ao_root + "system/file_system/preference",    	  data: {key: moduleName + "/" + configName,value:configValue},    	  success: function(data){},    	  async:true    	});    	return true;    }        static loadStorage(moduleName, configName){    	var result = "";    	$.ajax({    	  type: 'GET',    	  url: ao_root + "system/file_system/preference",    	  data: {key: moduleName + "/" + configName},    	  success: function(data){				if (data.error !== undefined){					result = "";				}else{					result = data;				}			  },    	  error: function(data){result = "";},    	  async:false,    	  timeout: 3000    	});    	return result;    }}class ao_module_codec{	//Decode umfilename into standard filename in utf-8, which umfilename usually start with "inith"	//Example: ao_module_codec.decodeUmFilename(umfilename_here);    static decodeUmFilename(umfilename){		if (umfilename.includes("inith")){			var data = umfilename.split(".");			if (data.length == 1){				//This is a filename without extension				data = data[0].replace("inith","");				var decodedname = ao_module_codec.decode_utf8(ao_module_codec.hex2bin(data));				if (decodedname != "false"){					//This is a umfilename					return decodedname;				}else{					//This is not a umfilename					return umfilename;				}			}else{				//This is a filename with extension				var extension = data.pop();				var filename = data[0];				filename = filename.replace("inith",""); //Javascript replace only remove the first instances (i.e. the first inith in filename)				var decodedname = ao_module_codec.decode_utf8(ao_module_codec.hex2bin(filename));				if (decodedname != "false"){					//This is a umfilename					return decodedname + "." + extension;				}else{					//This is not a umfilename					return umfilename;				}			}					}else{			//This is not umfilename as it doesn't have the inith prefix			return umfilename;		}	}		//Encode filename to UMfilename	//Example: ao_module_codec.encodeUMFilename("test.stl");	static encodeUMFilename(filename){	    if (filename.substring(0,5) != "inith"){	        //Check if the filename include extension. 	        if (filename.includes(".")){	            //Filename with extension. pop it out first.	            var info = filename.split(".");	            var ext = info.pop();	            var filenameOnly = info.join(".");	            var encodedFilename = "inith" + ao_module_codec.decode_utf8(ao_module_codec.bin2hex(filenameOnly)) + "." + ext;	            return encodedFilename;	        }else{	            //Filename with no extension. Convert the whole name into UMfilename	            var encodedFilename = "inith" + ao_module_codec.decode_utf8(ao_module_codec.bin2hex(filename));	            return encodedFilename;	        }	    }else{	        //This is already a UMfilename. return the raw filename.	        return filename;	    }	}		//Decode hexFoldername into standard foldername in utf-8, return the original name if it is not a hex foldername	//Example: ao_module_codec.decodeHexFoldername(hexFolderName_here);	static decodeHexFoldername(folderName, prefix=true){	    var decodedFoldername = ao_module_codec.decode_utf8(ao_module_codec.hex2bin(folderName));		if (decodedFoldername == "false"){			//This is not a hex encoded foldername			decodedFoldername = folderName;		}else{			//This is a hex encoded foldername			if (prefix){			    	decodedFoldername = "*" + decodedFoldername;			}else{			    	decodedFoldername =decodedFoldername;			}		}		return decodedFoldername;	}        //Encode foldername into hexfoldername    //Example: ao_module_codec.encodeHexFoldername("test");    static encodeHexFoldername(folderName){        var encodedFilename = "";        if (ao_module_codec.decodeHexFoldername(folderName) == folderName){            //This is not hex foldername. Encode it            encodedFilename = ao_module_codec.decode_utf8(ao_module_codec.bin2hex(folderName));        }else{            //This folder name already encoded. Return the original value            encodedFilename = folderName;        }                return encodedFilename;    }    static hex2bin(s){      var ret = []      var i = 0      var l      s += ''      for (l = s.length; i < l; i += 2) {        var c = parseInt(s.substr(i, 1), 16)        var k = parseInt(s.substr(i + 1, 1), 16)        if (isNaN(c) || isNaN(k)) return false        ret.push((c << 4) | k)      }          return String.fromCharCode.apply(String, ret)    }        static bin2hex(s){         var i          var l          var o = ''          var n          s += ''          for (i = 0, l = s.length; i < l; i++) {            n = s.charCodeAt(i)              .toString(16)            o += n.length < 2 ? '0' + n : n          }          return o    }        static decode_utf8(s) {      return decodeURIComponent(escape(s));    }}/**ArOZ Online Module Utils for quick deploy of ArOZ Online WebAppsao_module_utils.objectToAttr(object); //object to DOM attrao_module_utils.attrToObject(attr); //DOM attr to Objectao_module_utils.getRandomUID(); //Get random UUID from timestampao_module_utils.getIconFromExt(ext); //Get icon tag from file extensionao_module_utils.getDropFileInfo(dropEvent); //Get the filepath and filename list from file explorer drag dropao_module_utils.formatBytes(byte, decimals); //Format file byte size to human readable size **/class ao_module_utils{        //Two simple functions for converting any Javascript object into string that can be put into the attr value of an DOM object    static objectToAttr(object){       return encodeURIComponent(JSON.stringify(object));    }        static attrToObject(attr){        return JSON.parse(decodeURIComponent(attr));    }        //Get a random id for a new floatWindow, use with var uid = ao_module_utils.getRandomUID();    static getRandomUID(){        return new Date().getTime();    }    static stringToBlob(text, mimetype="text/plain"){        var blob = new Blob([text], { type: mimetype });        return blob    }    static blobToFile(blob, filename, mimetype="text/plain"){        var file = new File([blob], filename, {type: mimetype});        return file    }        //Get the icon of a file with given extension (ext), use with ao_module_utils.getIconFromExt("ext");    static getIconFromExt(ext){        var ext = ext.toLowerCase().trim();        var iconList={            md:"file text outline",            txt:"file text outline",            pdf:"file pdf outline",            doc:"file word outline",            docx:"file word outline",            odt:"file word outline",            xlsx:"file excel outline",            ods:"file excel outline",            ppt:"file powerpoint outline",            pptx:"file powerpoint outline",            odp:"file powerpoint outline",            jpg:"file image outline",            png:"file image outline",            jpeg:"file image outline",            gif:"file image outline",            odg:"file image outline",            psd:"file image outline",            zip:"file archive outline",            '7z':"file archive outline",            rar:"file archive outline",            tar:"file archive outline",            mp3:"file audio outline",            m4a:"file audio outline",            flac:"file audio outline",            wav:"file audio outline",            aac:"file audio outline",            mp4:"file video outline",            webm:"file video outline",            php:"file code outline",            html:"file code outline",            htm:"file code outline",            js:"file code outline",            css:"file code outline",            xml:"file code outline",            json:"file code outline",            csv:"file code outline",            odf:"file code outline",            bmp:"file image outline",            rtf:"file text outline",            wmv:"file video outline",            mkv:"file video outline",            ogg:"file audio outline",            stl:"cube",            obj:"cube",            "3ds":"cube",            fbx:"cube",            collada:"cube",            step:"cube",            iges:"cube",            gcode:"cube",            shortcut:"external square",            opus:"file audio outline",            apscene:"cubes"        };        var icon = "";        if (ext == ""){            icon = "folder outline";        }else{            icon = iconList[ext];            if (icon == undefined){                icon = "file outline"            }        }        return icon;	}		//Get the drop file properties {filepath: xxx, filename: xxx} from file drop events from file exploere	static getDropFileInfo(dropEvent){		if (dropEvent.dataTransfer.getData("filedata") !== ""){			var filelist = dropEvent.dataTransfer.getData("filedata");			filelist = JSON.parse(filelist);			return filelist;		}    }    static readFileFromFileObject(fileObject, successCallback, failedCallback=undefined){        let reader = new FileReader();        reader.readAsText(fileObject);        reader.onload = function() {            successCallback(reader.result);        };        reader.onerror = function() {            if (failedCallback != undefined){                failedCallback(reader.error);            }else{                console.log(reader.error);            }                   };    }        static formatBytes(a,b=2){if(0===a)return"0 Bytes";const c=0>b?0:b,d=Math.floor(Math.log(a)/Math.log(1024));return parseFloat((a/Math.pow(1024,d)).toFixed(c))+" "+["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"][d]}}
 |