var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
    for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
    for (var r = Array(s), k = 0, i = 0; i < il; i++)
        for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
            r[k] = a[j];
    return r;
};
var __classPrivateFieldSet = (undefined && undefined.__classPrivateFieldSet) || function (receiver, privateMap, value) {
    if (!privateMap.has(receiver)) {
        throw new TypeError("attempted to set private field on non-instance");
    }
    privateMap.set(receiver, value);
    return value;
};
var __classPrivateFieldGet = (undefined && undefined.__classPrivateFieldGet) || function (receiver, privateMap) {
    if (!privateMap.has(receiver)) {
        throw new TypeError("attempted to get private field on non-instance");
    }
    return privateMap.get(receiver);
};
var _data, _observer, _queue, _serverCancel, _serverDelete, _serverRename, _serverUpload;
var FileManager = /** @class */ (function () {
    function FileManager(handlers) {
        var _this = this;
        _data.set(this, new Map());
        _observer.set(this, new Map());
        _queue.set(this, new Map());
        _serverCancel.set(this, void 0);
        _serverDelete.set(this, void 0);
        _serverRename.set(this, void 0);
        _serverUpload.set(this, void 0);
        this.cancel = function (id) { return __awaiter(_this, void 0, void 0, function () {
            var queue;
            return __generator(this, function (_b) {
                queue = __classPrivateFieldGet(this, _queue).get(id);
                if (queue) {
                    __classPrivateFieldGet(this, _queue).delete(id);
                    this.executeObservers([id]);
                }
                return [2 /*return*/, __classPrivateFieldGet(this, _serverCancel).call(this, id)];
            });
        }); };
        this.delete = function (id, metadata) { return __awaiter(_this, void 0, void 0, function () {
            var queue, data;
            return __generator(this, function (_b) {
                queue = __classPrivateFieldGet(this, _queue).get(id);
                data = __classPrivateFieldGet(this, _data).get(id);
                if (queue)
                    __classPrivateFieldGet(this, _queue).delete(id);
                if (data)
                    __classPrivateFieldGet(this, _data).delete(id);
                if (queue || data)
                    this.executeObservers([id]);
                return [2 /*return*/, __classPrivateFieldGet(this, _serverDelete).call(this, id, metadata)];
            });
        }); };
        this.rename = function (id, name) { return __awaiter(_this, void 0, void 0, function () {
            var queue, data;
            return __generator(this, function (_b) {
                queue = __classPrivateFieldGet(this, _queue).get(id);
                data = __classPrivateFieldGet(this, _data).get(id);
                if (queue)
                    __classPrivateFieldGet(this, _queue).set(id, Object.assign(Object.assign({}, queue), { file: Object.assign(Object.assign({}, queue.file), { name: name }) }));
                if (data)
                    __classPrivateFieldGet(this, _data).set(id, Object.assign(Object.assign({}, data), { name: name }));
                if (queue || data)
                    this.executeObservers([id]);
                return [2 /*return*/, __classPrivateFieldGet(this, _serverRename).call(this, id, name)];
            });
        }); };
        this.upload = function (file, metadata) { return __awaiter(_this, void 0, void 0, function () {
            var id, name, type, hash, url, queued, partial, ref;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        id = file.id, name = file.name, type = file.type;
                        return [4 /*yield*/, file.hash()];
                    case 1:
                        hash = _b.sent();
                        url = URL.createObjectURL(file.blob);
                        queued = { hash: hash, id: id, name: name, type: type, url: url };
                        __classPrivateFieldGet(this, _queue).set(file.id, { file: queued, progress: 0 });
                        this.executeObservers([id]);
                        return [4 /*yield*/, __classPrivateFieldGet(this, _serverUpload).call(this, file, metadata)];
                    case 2:
                        partial = _b.sent();
                        ref = Object.assign(Object.assign({}, queued), partial);
                        if (url !== ref.url)
                            URL.revokeObjectURL(queued.url);
                        __classPrivateFieldGet(this, _queue).set(file.id, { file: ref, progress: 1 });
                        this.executeObservers([id]);
                        return [2 /*return*/, ref];
                }
            });
        }); };
        __classPrivateFieldSet(this, _serverCancel, handlers.cancel);
        __classPrivateFieldSet(this, _serverDelete, handlers.delete);
        __classPrivateFieldSet(this, _serverRename, handlers.rename);
        __classPrivateFieldSet(this, _serverUpload, handlers.upload);
    }
    FileManager.inMemory = function (options) {
        if (options === void 0) { options = {}; }
        return createInMemoryFileManager(Object.assign({ delay: {} }, options));
    };
    FileManager.prototype.get = function (id) {
        return __classPrivateFieldGet(this, _data).get(id);
    };
    FileManager.prototype.getIds = function () {
        // const files = [...this.#data.keys(), ...this.#queue.keys()];
        var files = [];
        // Using simple loops for compatibility
        __classPrivateFieldGet(this, _data).forEach(function (_, key) { return files.push(key); });
        __classPrivateFieldGet(this, _queue).forEach(function (_, key) { return files.push(key); });
        return Array.from(new Set(files));
    };
    FileManager.prototype.setFiles = function (files) {
        var _this = this;
        var data = new Map();
        for (var _i = 0, files_1 = files; _i < files_1.length; _i++) {
            var file = files_1[_i];
            __classPrivateFieldGet(this, _queue).delete(file.id);
            data.set(file.id, file);
        }
        var ids = this.getIds();
        var changes = ids.filter(function (id) {
            var prev = __classPrivateFieldGet(_this, _data).get(id);
            var next = data.get(id);
            return !_this.isEqual(prev, next);
        });
        __classPrivateFieldSet(this, _data, data);
        if (changes.length)
            this.executeObservers(changes);
    };
    FileManager.prototype.setProgress = function (id, progress) {
        var queue = __classPrivateFieldGet(this, _queue).get(id);
        if (!queue)
            return;
        __classPrivateFieldGet(this, _queue).set(id, Object.assign(Object.assign({}, queue), { progress: progress }));
        this.executeObservers([id]);
    };
    FileManager.prototype.subscribe = function (observer, filter) {
        var _this = this;
        __classPrivateFieldGet(this, _observer).set(observer, filter);
        observer(this.getDataObject(filter));
        return function () { return _this.unsubscribe(observer); };
    };
    FileManager.prototype.unsubscribe = function (observer) {
        __classPrivateFieldGet(this, _observer).delete(observer);
    };
    FileManager.prototype.executeObservers = function (ids) {
        var _this = this;
        __classPrivateFieldGet(this, _observer).forEach(function (filter, observer) {
            if (!filter || ids.some(function (id) { return filter.indexOf(id) >= 0; }))
                observer(_this.getDataObject(filter));
        });
    };
    FileManager.prototype.getDataObject = function (filter) {
        var _this = this;
        var files = this.getIds();
        return files.reduce(function (acc, id) {
            var _a;
            if (filter && filter.indexOf(id) < 0)
                return acc;
            var data = __classPrivateFieldGet(_this, _data).get(id);
            var queue = __classPrivateFieldGet(_this, _queue).get(id);
            acc[id] = { file: data || (queue === null || queue === void 0 ? void 0 : queue.file), progress: (_a = queue === null || queue === void 0 ? void 0 : queue.progress) !== null && _a !== void 0 ? _a : 1 };
            return acc;
        }, {});
    };
    FileManager.prototype.isEqual = function (ref1, ref2) {
        // Check existence
        if (ref1 == undefined || ref2 == undefined)
            return ref1 === ref2;
        // Check change in progress
        if (typeof ref1 === 'number' || typeof ref2 === 'number')
            return ref1 === ref2;
        // Check change in file structure
        for (var prop in ref1) {
            if (ref1[prop] !== ref2[prop])
                return false;
        }
        return true;
    };
    return FileManager;
}());
_data = new WeakMap(), _observer = new WeakMap(), _queue = new WeakMap(), _serverCancel = new WeakMap(), _serverDelete = new WeakMap(), _serverRename = new WeakMap(), _serverUpload = new WeakMap();
var createInMemoryFileManager = function (options) {
    var tasks = new Map();
    var files = new Map();
    var cancel = function (id) { return __awaiter(void 0, void 0, void 0, function () {
        var task;
        return __generator(this, function (_b) {
            task = tasks.get(id);
            if (!task)
                return [2 /*return*/];
            window.clearTimeout(task);
            tasks.delete(id);
            return [2 /*return*/];
        });
    }); };
    var doDelete = function (id) { return __awaiter(void 0, void 0, void 0, function () {
        return __generator(this, function (_b) {
            files.delete(id);
            update();
            return [2 /*return*/];
        });
    }); };
    var rename = function (id, name) { return __awaiter(void 0, void 0, void 0, function () {
        var file;
        return __generator(this, function (_b) {
            file = files.get(id);
            if (!file)
                return [2 /*return*/];
            files.set(file.id, Object.assign(Object.assign({}, file), { name: name }));
            update();
            return [2 /*return*/];
        });
    }); };
    var upload = function (file) {
        var _b = options.delay, _c = _b.min, min = _c === void 0 ? 2000 : _c, _d = _b.max, max = _d === void 0 ? 5000 : _d;
        return new Promise(function (res) {
            var delay = Math.random() * (max - min + 1) + min;
            var timer = window.setTimeout(function () { return __awaiter(void 0, void 0, void 0, function () {
                var ref, _b;
                return __generator(this, function (_c) {
                    switch (_c.label) {
                        case 0:
                            _b = {};
                            return [4 /*yield*/, file.hash()];
                        case 1:
                            ref = (_b.hash = _c.sent(),
                                _b.id = file.id,
                                _b.name = file.name,
                                _b.type = file.type,
                                _b.url = URL.createObjectURL(file.blob),
                                _b);
                            tasks.delete(file.id);
                            files.set(file.id, ref);
                            res(ref);
                            update();
                            return [2 /*return*/];
                    }
                });
            }); }, delay);
            tasks.set(file.id, timer);
        });
    };
    var update = function () {
        manager.setFiles(Array.from(files.values()));
    };
    var manager = new FileManager({ cancel: cancel, delete: doDelete, rename: rename, upload: upload });
    return manager;
};
var ScrollSpy = /** @class */ (function () {
    function ScrollSpy(selector, options) {
        var _this = this;
        if (options === void 0) { options = {}; }
        this.current = null;
        this.nodes = [];
        this.offset = 0;
        this.timeout = 0;
        this.handleScroll = function () {
            if (_this.timeout)
                window.cancelAnimationFrame(_this.timeout);
            _this.timeout = window.requestAnimationFrame(function () { return _this.detect(); });
        };
        this.selector = selector;
        this.options = Object.assign({ class: 'active', scroll: window }, options);
        this.reload();
        window.addEventListener('resize', this.handleScroll);
        this.options.scroll.addEventListener('scroll', this.handleScroll);
    }
    Object.defineProperty(ScrollSpy.prototype, "active", {
        get: function () {
            var last = this.nodes[this.nodes.length - 1];
            if (last && this.bottom && this.isVisible(last.section))
                return last;
            for (var i = this.nodes.length - 1; i >= 0; i--)
                if (this.isVisible(this.nodes[i].section))
                    return this.nodes[i];
            return null;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ScrollSpy.prototype, "bottom", {
        get: function () {
            var r = this.root;
            return r.scrollHeight - window.innerHeight <= r.scrollTop;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ScrollSpy.prototype, "root", {
        get: function () {
            return (document.scrollingElement || document.documentElement);
        },
        enumerable: true,
        configurable: true
    });
    ScrollSpy.prototype.destroy = function () {
        if (this.current)
            this.deactivate(this.current);
        window.removeEventListener('resize', this.handleScroll);
        this.options.scroll.removeEventListener('scroll', this.handleScroll);
    };
    ScrollSpy.prototype.detect = function () {
        var active = this.active;
        var current = this.current;
        if (current) {
            if (current === active)
                return;
            else
                this.deactivate(current);
        }
        if (active)
            this.activate(active);
        this.current = active;
    };
    ScrollSpy.prototype.reload = function () {
        var r = this.root;
        var elements = r.querySelectorAll(this.selector);
        this.nodes = Array.from(elements).reduce(function (acc, el) {
            var hash = ('hash' in el && el.hash) || '';
            var id = decodeURIComponent(hash.slice(1));
            var section = document.getElementById(id);
            return section ? __spreadArrays(acc, [{ nav: el, section: section }]) : acc;
        }, []);
        this.offset = Math.min.apply(Math, this.nodes.map(function (node) { return node.section.offsetTop; }));
        this.detect();
    };
    ScrollSpy.prototype.activate = function (node) {
        node.nav.classList.add(this.options.class);
    };
    ScrollSpy.prototype.deactivate = function (node) {
        node.nav.classList.remove(this.options.class);
    };
    ScrollSpy.prototype.isVisible = function (el, bottom) {
        var rect = el.getBoundingClientRect();
        return bottom ? rect.bottom < window.innerHeight : rect.top <= this.offset;
    };
    return ScrollSpy;
}());
export { FileManager as F, ScrollSpy as S };
