"use strict"; import {Common} from "../Utils/Common.ecma.js"; import {Check} from "../Utils/Checks.ecma.js"; /** * @typedef {import("../Application/AnP.ecma.js").AnP} AnP */ /** * @class FilesDriver * @constructor * @param {!AnP} anp * @param {?(Object.|Array)} [inputs = null] * @returns {void} * @access private * @static */ export const FilesDriver = (function(){ /** * @callback simple_callback * @returns {void} */ /** * @callback continue_callback * @param {!boolean} ok * @return {boolean} */ /** * @callback ok_callback * @param {!boolean} ok * @return {void} */ /** * @callback load_callback * @param {?string} content * @param {!boolean} ok * @return {void} */ /** * @callback load_json_callback * @param {!Array.|Array.>} results * @return {void} */ /** * @constructs FilesDriver * @param {!AnP} anp * @param {?(Object.|Array)} [inputs = null] * @returns {void} * @access private * @static */ const FilesDriver = function(anp, inputs = null){ /** @type {FilesDriver} */ const self = this; /** @type {Array.} */ let default_root_urls = [""], /** @type {number} */ default_timeout = 2000, /** @type {boolean} */ default_asynchronous = true, /** @type {string} */ default_method = "get", /** @type {boolean} */ started = false; /** * @returns {void} * @access private */ const constructor = () => {}; /** * @param {?continue_callback} callback * @returns {boolean} * @access public */ this.update = (callback = null) => { default_timeout = anp.settings.get(["files_driver_timeout", "timeout"], inputs, default_timeout); default_asynchronous = anp.settings.get(["files_driver_asynchronous", "asynchronous"], inputs, default_asynchronous); default_method = anp.settings.get(["files_driver_method", "method"], inputs, default_method); Common.execute(callback); }; /** * @param {?continue_callback} callback * @returns {boolean} * @access public */ this.reset = (callback = null) => { default_root_urls = [""]; default_timeout = 2000; default_asynchronous = true; self.update(callback); }; /** * @param {?continue_callback} callback * @returns {boolean} * @access public */ this.start = (callback = null) => { /** @type {continue_callback} */ const end = ok => Common.execute(callback, ok); if(started){ end(false); return false; }; started = true; self.update(() => end(true)); return true; }; /** * @param {?continue_callback} callback * @returns {boolean} * @access public */ this.close = (callback = null) => { /** @type {continue_callback} */ const end = ok => Common.execute(callback, ok); if(!started){ end(false); return false; }; started = false; end(true); return true; }; /** * @param {!string} method * @param {!string} url * @param {!boolean} asynchronous * @param {!number} timeout * @param {?load_callback} [callback = null] * @param {!Array.} root_urls * @param {!number} [i = 0] * @param {!Array.} [root_urls_done = []] * @return {void} * @access public */ const load_url_route = (method, url, asynchronous, timeout, callback, root_urls, i = 0, root_urls_done = []) => { if(i == root_urls.length){ Common.execute(callback, null, false); return; }; /** @type {simple_callback} */ const next = () => { load_url_route(method, url, asynchronous, timeout, callback, root_urls, i + 1, root_urls_done.concat(root_urls[i])); }; if(root_urls_done.includes(root_urls[i])){ next(); return; }; /** @type {boolean} */ let ended = false; /** @type {XMLHttpRequest} */ const ajax = new XMLHttpRequest(), /** @type {ok_callback} */ end = (ok = false) => { if(ended) return; ended = true; if(ok) Common.execute(callback, ajax.responseText, true); else next(); }, /** @type {number} */ date = Date.now(); ajax.open(method, root_urls[i] + url, asynchronous); ajax.timeout = timeout; ajax.onreadystatechange = () => { if(ended) return; if(ajax.readyState == 4) end((ajax.status >= 200 && ajax.status < 300) || [301, 302, 304].includes(ajax.status)); else if(Date.now() - date >= timeout) end(false); }; ajax.send(null); ajax.onload = end; ajax.onerror = end; ajax.ontimeout = end; }; /** * @param {!string} url * @param {?load_callback} [callback = null] * @param {?(Object.|Array.)} [inputs = null] * @return {void} * @access public */ this.load = (url, callback, inputs = null) => { load_url_route( Common.get_value(["files_driver_method", "method"], inputs, "get").toLowerCase(), url, Common.get_value(["files_driver_asynchronous", "asynchronous"], inputs, default_asynchronous), Common.get_value(["files_driver_timeout", "timeout"], inputs, default_timeout), callback, Common.get_value(["files_driver_root_urls", "root_urls"], inputs, []).concat(default_root_urls) ); }; /** * @param {?any} data * @param {!load_json_callback} callback * @param {?(Object.|Array.)} [inputs = null] * @return {void} * @access public */ this.load_json = (data, callback, inputs = null) => { if(Check.is_bool(inputs)) inputs = {only_dictionaries : inputs}; /** @type {boolean} */ const only_dictionaries = Common.get_value([ "files_driver_load_json_only_dictionaries", "load_json_only_dictionaries", "only_dictionaries" ], Check.is_bool(inputs) ? inputs = { only_dictionaries : inputs } : inputs, false), /** @type {Array.|Array.>} */ results = [], /** @type {simple_callback} */ end = () => { Common.execute(callback, results); }; if(Check.is_dictionary(data)){ results.push(data); end(); }else if(Check.is_array(data)){ if(only_dictionaries){ /** @type {number} */ const l = data.length; /** @type {number} */ let loaded = 0; for(let item of data) self.load_json(item, subresults => { results.push(...subresults); ++ loaded == l && end(); }, inputs); }else{ results.push(data); end(); }; }else if(Check.is_string(data)){ /** @type {Object.|Array.|null} */ let json; if(Check.is_json(data, false) && (json = JSON.parse(data)) !== null) self.load_json(json, subresults => { results.push(...subresults); end(); }, inputs); else self.load(data, (content, ok) => { self.load_json(content, subresults => { results.push(...subresults); end(); }, inputs); }, inputs); }else end(); }; constructor(); }; return FilesDriver; })();