' +
- dayNames.map(function (name, i) {
- return (
- '' + dayNames[(i + dayOffset) % dayNames.length] + ''
- );
- }).join('') +
- mapDays(hilightedDate, dayOffset, function (date) {
- var isNotInMonth = date.getMonth() !== hilightedMonth;
- var isDisabled = !opts.inRange(date);
- var isToday = date.getTime() === today;
- var className = 'dp-day';
- className += (isNotInMonth ? ' dp-edge-day' : '');
- className += (datesEq(date, hilightedDate) ? ' dp-current' : '');
- className += (datesEq(date, selectedDate) ? ' dp-selected' : '');
- className += (isDisabled ? ' dp-day-disabled' : '');
- className += (isToday ? ' dp-day-today' : '');
- className += ' ' + opts.dateClass(date, dp);
+function hookCreatingChain(f1, f2) {
+ // Enables chained events that takes several arguments and may modify first argument by making a modification and then returning the same instance.
+ // This pattern is used in the hook("creating") event.
+ if (f1 === nop) return f2;
+ return function () {
+ var res = f1.apply(this, arguments);
+ if (res !== undefined) arguments[0] = res;
+ var onsuccess = this.onsuccess,
+ // In case event listener has set this.onsuccess
+ onerror = this.onerror; // In case event listener has set this.onerror
+
+ this.onsuccess = null;
+ this.onerror = null;
+ var res2 = f2.apply(this, arguments);
+ if (onsuccess) this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess;
+ if (onerror) this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror;
+ return res2 !== undefined ? res2 : res;
+ };
+}
- return (
- ''
- );
- }) +
- '
' +
- '' +
- '
'
- );
- }
+function hookDeletingChain(f1, f2) {
+ if (f1 === nop) return f2;
+ return function () {
+ f1.apply(this, arguments);
+ var onsuccess = this.onsuccess,
+ // In case event listener has set this.onsuccess
+ onerror = this.onerror; // In case event listener has set this.onerror
+
+ this.onsuccess = this.onerror = null;
+ f2.apply(this, arguments);
+ if (onsuccess) this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess;
+ if (onerror) this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror;
+ };
+}
- /**
- * keyDown handles the key down event for the day-picker
- *
- * @param {Event} e
- * @param {DatePickerContext} dp
- */
- function keyDown(e, dp) {
- var key = e.keyCode;
- var shiftBy =
- (key === Key.left) ? -1 :
- (key === Key.right) ? 1 :
- (key === Key.up) ? -7 :
- (key === Key.down) ? 7 :
- 0;
+function hookUpdatingChain(f1, f2) {
+ if (f1 === nop) return f2;
+ return function (modifications) {
+ var res = f1.apply(this, arguments);
+ extend(modifications, res); // If f1 returns new modifications, extend caller's modifications with the result before calling next in chain.
- if (key === Key.esc) {
- dp.close();
- } else if (shiftBy) {
- e.preventDefault();
- dp.setState({
- hilightedDate: shiftDay(dp.state.hilightedDate, shiftBy)
+ var onsuccess = this.onsuccess,
+ // In case event listener has set this.onsuccess
+ onerror = this.onerror; // In case event listener has set this.onerror
+
+ this.onsuccess = null;
+ this.onerror = null;
+ var res2 = f2.apply(this, arguments);
+ if (onsuccess) this.onsuccess = this.onsuccess ? callBoth(onsuccess, this.onsuccess) : onsuccess;
+ if (onerror) this.onerror = this.onerror ? callBoth(onerror, this.onerror) : onerror;
+ return res === undefined ? res2 === undefined ? undefined : res2 : extend(res, res2);
+ };
+}
+
+function reverseStoppableEventChain(f1, f2) {
+ if (f1 === nop) return f2;
+ return function () {
+ if (f2.apply(this, arguments) === false) return false;
+ return f1.apply(this, arguments);
+ };
+}
+
+function promisableChain(f1, f2) {
+ if (f1 === nop) return f2;
+ return function () {
+ var res = f1.apply(this, arguments);
+
+ if (res && typeof res.then === 'function') {
+ var thiz = this,
+ i = arguments.length,
+ args = new Array(i);
+
+ while (i--) {
+ args[i] = arguments[i];
+ }
+
+ return res.then(function () {
+ return f2.apply(thiz, args);
});
}
- }
- function selectToday(e, dp) {
- dp.setState({
- selectedDate: now(),
- });
- }
+ return f2.apply(this, arguments);
+ };
+}
+/*
+ * Copyright (c) 2014-2017 David Fahlander
+ * Apache License Version 2.0, January 2004, http://www.apache.org/licenses/LICENSE-2.0
+ */
+//
+// Promise and Zone (PSD) for Dexie library
+//
+// I started out writing this Promise class by copying promise-light (https://github.com/taylorhakes/promise-light) by
+// https://github.com/taylorhakes - an A+ and ECMASCRIPT 6 compliant Promise implementation.
+//
+// In previous versions this was fixed by not calling setTimeout when knowing that the resolve() or reject() came from another
+// tick. In Dexie v1.4.0, I've rewritten the Promise class entirely. Just some fragments of promise-light is left. I use
+// another strategy now that simplifies everything a lot: to always execute callbacks in a new micro-task, but have an own micro-task
+// engine that is indexedDB compliant across all browsers.
+// Promise class has also been optimized a lot with inspiration from bluebird - to avoid closures as much as possible.
+// Also with inspiration from bluebird, asyncronic stacks in debug mode.
+//
+// Specific non-standard features of this Promise class:
+// * Custom zone support (a.k.a. PSD) with ability to keep zones also when using native promises as well as
+// native async / await.
+// * Promise.follow() method built upon the custom zone engine, that allows user to track all promises created from current stack frame
+// and below + all promises that those promises creates or awaits.
+// * Detect any unhandled promise in a PSD-scope (PSD.onunhandled).
+//
+// David Fahlander, https://github.com/dfahlander
+//
+// Just a pointer that only this module knows about.
+// Used in Promise constructor to emulate a private constructor.
- function clear(e, dp) {
- dp.setState({
- selectedDate: null,
- });
- }
- function close(e, dp) {
- dp.close();
- }
+var INTERNAL = {}; // Async stacks (long stacks) must not grow infinitely.
- function showMonthPicker(e, dp) {
- dp.setState({
- view: 'month'
- });
- }
+var LONG_STACKS_CLIP_LIMIT = 100;
+var MAX_LONG_STACKS = 20;
+var ZONE_ECHO_LIMIT = 7;
- function showYearPicker(e, dp) {
- dp.setState({
- view: 'year'
- });
+var nativePromiseInstanceAndProto = function () {
+ try {
+ // Be able to patch native async functions
+ return new Function("let F=async ()=>{},p=F();return [p,Object.getPrototypeOf(p),Promise.resolve(),F.constructor];")();
+ } catch (e) {
+ var P = _global.Promise;
+ return P ? [P.resolve(), P.prototype, P.resolve()] : [];
}
+}();
- function gotoNextMonth(e, dp) {
- var hilightedDate = dp.state.hilightedDate;
- dp.setState({
- hilightedDate: shiftMonth(hilightedDate, 1)
- });
- }
+var resolvedNativePromise = nativePromiseInstanceAndProto[0];
+var nativePromiseProto = nativePromiseInstanceAndProto[1];
+var resolvedGlobalPromise = nativePromiseInstanceAndProto[2];
+var nativePromiseThen = nativePromiseProto && nativePromiseProto.then;
+var NativePromise = resolvedNativePromise && resolvedNativePromise.constructor;
+var AsyncFunction = nativePromiseInstanceAndProto[3];
+var patchGlobalPromise = !!resolvedGlobalPromise;
+var stack_being_generated = false;
+/* The default function used only for the very first promise in a promise chain.
+ As soon as then promise is resolved or rejected, all next tasks will be executed in micro ticks
+ emulated in this module. For indexedDB compatibility, this means that every method needs to
+ execute at least one promise before doing an indexedDB operation. Dexie will always call
+ db.ready().then() for every operation to make sure the indexedDB event is started in an
+ indexedDB-compatible emulated micro task loop.
+*/
+
+var schedulePhysicalTick = resolvedGlobalPromise ? function () {
+ resolvedGlobalPromise.then(physicalTick);
+} : _global.setImmediate ? // setImmediate supported. Those modern platforms also supports Function.bind().
+setImmediate.bind(null, physicalTick) : _global.MutationObserver ? // MutationObserver supported
+function () {
+ var hiddenDiv = document.createElement("div");
+ new MutationObserver(function () {
+ physicalTick();
+ hiddenDiv = null;
+ }).observe(hiddenDiv, {
+ attributes: true
+ });
+ hiddenDiv.setAttribute('i', '1');
+} : // No support for setImmediate or MutationObserver. No worry, setTimeout is only called
+// once time. Every tick that follows will be our emulated micro tick.
+// Could have uses setTimeout.bind(null, 0, physicalTick) if it wasnt for that FF13 and below has a bug
+function () {
+ setTimeout(physicalTick, 0);
+}; // Configurable through Promise.scheduler.
+// Don't export because it would be unsafe to let unknown
+// code call it unless they do try..catch within their callback.
+// This function can be retrieved through getter of Promise.scheduler though,
+// but users must not do Promise.scheduler = myFuncThatThrowsException
- function gotoPrevMonth(e, dp) {
- var hilightedDate = dp.state.hilightedDate;
- dp.setState({
- hilightedDate: shiftMonth(hilightedDate, -1)
- });
+var asap$1 = function (callback, args) {
+ microtickQueue.push([callback, args]);
+
+ if (needsNewPhysicalTick) {
+ schedulePhysicalTick();
+ needsNewPhysicalTick = false;
}
+};
- function selectDay(e, dp) {
- dp.setState({
- selectedDate: new Date(parseInt(e.target.getAttribute('data-date'))),
+var isOutsideMicroTick = true;
+var needsNewPhysicalTick = true;
+var unhandledErrors = [];
+var rejectingErrors = [];
+var currentFulfiller = null;
+var rejectionMapper = mirror; // Remove in next major when removing error mapping of DOMErrors and DOMExceptions
+
+var globalPSD = {
+ id: 'global',
+ global: true,
+ ref: 0,
+ unhandleds: [],
+ onunhandled: globalError,
+ pgp: false,
+ env: {},
+ finalize: function () {
+ this.unhandleds.forEach(function (uh) {
+ try {
+ globalError(uh[0], uh[1]);
+ } catch (e) {}
});
}
+};
+var PSD = globalPSD;
+var microtickQueue = []; // Callbacks to call in this or next physical tick.
- function mapDays(currentDate, dayOffset, fn) {
- var result = '';
- var iter = new Date(currentDate);
- iter.setDate(1);
- iter.setDate(1 - iter.getDay() + dayOffset);
+var numScheduledCalls = 0; // Number of listener-calls left to do in this physical tick.
- // If we are showing monday as the 1st of the week,
- // and the monday is the 2nd of the month, the sunday won't
- // show, so we need to shift backwards
- if (dayOffset && iter.getDate() === dayOffset + 1) {
- iter.setDate(dayOffset - 6);
- }
+var tickFinalizers = []; // Finalizers to call when there are no more async calls scheduled within current physical tick.
- // We are going to have 6 weeks always displayed to keep a consistent
- // calendar size
- for (var day = 0; day < (6 * 7); ++day) {
- result += fn(iter);
- iter.setDate(iter.getDate() + 1);
- }
+function Promise(fn) {
+ if (_typeof(this) !== 'object') throw new TypeError('Promises must be constructed via new');
+ this._listeners = [];
+ this.onuncatched = nop; // Deprecate in next major. Not needed. Better to use global error handler.
+ // A library may set `promise._lib = true;` after promise is created to make resolve() or reject()
+ // execute the microtask engine implicitely within the call to resolve() or reject().
+ // To remain A+ compliant, a library must only set `_lib=true` if it can guarantee that the stack
+ // only contains library code when calling resolve() or reject().
+ // RULE OF THUMB: ONLY set _lib = true for promises explicitely resolving/rejecting directly from
+ // global scope (event handler, timer etc)!
- return result;
+ this._lib = false; // Current async scope
+
+ var psd = this._PSD = PSD;
+
+ if (debug) {
+ this._stackHolder = getErrorWithStack();
+ this._prev = null;
+ this._numPrev = 0; // Number of previous promises (for long stacks)
}
- /**
- * @file Manages the month-picker view.
- */
+ if (typeof fn !== 'function') {
+ if (fn !== INTERNAL) throw new TypeError('Not a function'); // Private constructor (INTERNAL, state, value).
+ // Used internally by Promise.resolve() and Promise.reject().
- var monthPicker = {
- onKeyDown: keyDown$1,
- onClick: {
- 'dp-month': onChooseMonth
- },
- render: render$1
- };
+ this._state = arguments[1];
+ this._value = arguments[2];
+ if (this._state === false) handleRejection(this, this._value); // Map error, set stack and addPossiblyUnhandledError().
- function onChooseMonth(e, dp) {
- dp.setState({
- hilightedDate: setMonth(dp.state.hilightedDate, parseInt(e.target.getAttribute('data-month'))),
- view: 'day',
- });
+ return;
}
- /**
- * render renders the month picker as an HTML string
- *
- * @param {DatePickerContext} dp the date picker context
- * @returns {string}
- */
- function render$1(dp) {
- var opts = dp.opts;
- var lang = opts.lang;
- var months = lang.months;
- var currentDate = dp.state.hilightedDate;
- var currentMonth = currentDate.getMonth();
+ this._state = null; // null (=pending), false (=rejected) or true (=resolved)
- return (
- '
'
- );
- }
+ return stack;
+ } finally {
+ stack_being_generated = false;
+ }
+ }
+ },
+ timeout: function (ms, msg) {
+ var _this = this;
- function onChooseYear(e, dp) {
- dp.setState({
- hilightedDate: setYear(dp.state.hilightedDate, parseInt(e.target.getAttribute('data-year'))),
- view: 'day',
- });
+ return ms < Infinity ? new Promise(function (resolve, reject) {
+ var handle = setTimeout(function () {
+ return reject(new exceptions.Timeout(msg));
+ }, ms);
+
+ _this.then(resolve, reject).finally(clearTimeout.bind(null, handle));
+ }) : this;
}
+});
+if (typeof Symbol !== 'undefined' && Symbol.toStringTag) setProp(Promise.prototype, Symbol.toStringTag, 'Promise'); // Now that Promise.prototype is defined, we have all it takes to set globalPSD.env.
+// Environment globals snapshotted on leaving global zone
- function keyDown$2(e, dp) {
- var key = e.keyCode;
- var opts = dp.opts;
- var shiftBy =
- (key === Key.left || key === Key.up) ? 1 :
- (key === Key.right || key === Key.down) ? -1 :
- 0;
+globalPSD.env = snapShot();
- if (key === Key.esc) {
- dp.setState({
- view: 'day',
- });
- } else if (shiftBy) {
- e.preventDefault();
- var shiftedYear = shiftYear(dp.state.hilightedDate, shiftBy);
+function Listener(onFulfilled, onRejected, resolve, reject, zone) {
+ this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
+ this.onRejected = typeof onRejected === 'function' ? onRejected : null;
+ this.resolve = resolve;
+ this.reject = reject;
+ this.psd = zone;
+} // Promise Static Properties
- dp.setState({
- hilightedDate: constrainDate(shiftedYear, opts.min, opts.max),
+
+props(Promise, {
+ all: function () {
+ var values = getArrayOf.apply(null, arguments) // Supports iterables, implicit arguments and array-like.
+ .map(onPossibleParallellAsync); // Handle parallell async/awaits
+
+ return new Promise(function (resolve, reject) {
+ if (values.length === 0) resolve([]);
+ var remaining = values.length;
+ values.forEach(function (a, i) {
+ return Promise.resolve(a).then(function (x) {
+ values[i] = x;
+ if (! --remaining) resolve(values);
+ }, reject);
+ });
+ });
+ },
+ resolve: function (value) {
+ if (value instanceof Promise) return value;
+ if (value && typeof value.then === 'function') return new Promise(function (resolve, reject) {
+ value.then(resolve, reject);
+ });
+ var rv = new Promise(INTERNAL, true, value);
+ linkToPreviousPromise(rv, currentFulfiller);
+ return rv;
+ },
+ reject: PromiseReject,
+ race: function () {
+ var values = getArrayOf.apply(null, arguments).map(onPossibleParallellAsync);
+ return new Promise(function (resolve, reject) {
+ values.map(function (value) {
+ return Promise.resolve(value).then(resolve, reject);
});
+ });
+ },
+ PSD: {
+ get: function () {
+ return PSD;
+ },
+ set: function (value) {
+ return PSD = value;
+ }
+ },
+ //totalEchoes: {get: ()=>totalEchoes},
+ //task: {get: ()=>task},
+ newPSD: newScope,
+ usePSD: usePSD,
+ scheduler: {
+ get: function () {
+ return asap$1;
+ },
+ set: function (value) {
+ asap$1 = value;
}
+ },
+ rejectionMapper: {
+ get: function () {
+ return rejectionMapper;
+ },
+ set: function (value) {
+ rejectionMapper = value;
+ } // Map reject failures
+
+ },
+ follow: function (fn, zoneProps) {
+ return new Promise(function (resolve, reject) {
+ return newScope(function (resolve, reject) {
+ var psd = PSD;
+ psd.unhandleds = []; // For unhandled standard- or 3rd party Promises. Checked at psd.finalize()
+
+ psd.onunhandled = reject; // Triggered directly on unhandled promises of this library.
+
+ psd.finalize = callBoth(function () {
+ var _this = this; // Unhandled standard or 3rd part promises are put in PSD.unhandleds and
+ // examined upon scope completion while unhandled rejections in this Promise
+ // will trigger directly through psd.onunhandled
+
+
+ run_at_end_of_this_or_next_physical_tick(function () {
+ _this.unhandleds.length === 0 ? resolve() : reject(_this.unhandleds[0]);
+ });
+ }, psd.finalize);
+ fn();
+ }, zoneProps, resolve, reject);
+ });
}
+});
+/**
+* Take a potentially misbehaving resolver function and make sure
+* onFulfilled and onRejected are only called once.
+*
+* Makes no guarantees about asynchrony.
+*/
+
+function executePromiseTask(promise, fn) {
+ // Promise Resolution Procedure:
+ // https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+ try {
+ fn(function (value) {
+ if (promise._state !== null) return; // Already settled
- function mapYears(dp, fn) {
- var result = '';
- var max = dp.opts.max.getFullYear();
+ if (value === promise) throw new TypeError('A promise cannot be resolved with itself.');
+ var shouldExecuteTick = promise._lib && beginMicroTickScope();
- for (var i = max; i >= dp.opts.min.getFullYear(); --i) {
- result += fn(i);
- }
+ if (value && typeof value.then === 'function') {
+ executePromiseTask(promise, function (resolve, reject) {
+ value instanceof Promise ? value._then(resolve, reject) : value.then(resolve, reject);
+ });
+ } else {
+ promise._state = true;
+ promise._value = value;
+ propagateAllListeners(promise);
+ }
- return result;
+ if (shouldExecuteTick) endMicroTickScope();
+ }, handleRejection.bind(null, promise)); // If Function.bind is not supported. Exception is handled in catch below
+ } catch (ex) {
+ handleRejection(promise, ex);
}
+}
- /**
- * @file Defines the base date picker behavior, overridden by various modes.
- */
+function handleRejection(promise, reason) {
+ rejectingErrors.push(reason);
+ if (promise._state !== null) return;
+ var shouldExecuteTick = promise._lib && beginMicroTickScope();
+ reason = rejectionMapper(reason);
+ promise._state = false;
+ promise._value = reason;
+ debug && reason !== null && _typeof(reason) === 'object' && !reason._promise && tryCatch(function () {
+ var origProp = getPropertyDescriptor(reason, "stack");
+ reason._promise = promise;
+ setProp(reason, "stack", {
+ get: function () {
+ return stack_being_generated ? origProp && (origProp.get ? origProp.get.apply(reason) : origProp.value) : promise.stack;
+ }
+ });
+ }); // Add the failure to a list of possibly uncaught errors
- var views = {
- day: dayPicker,
- year: yearPicker,
- month: monthPicker
- };
+ addPossiblyUnhandledError(promise);
+ propagateAllListeners(promise);
+ if (shouldExecuteTick) endMicroTickScope();
+}
- function BaseMode(input, emit, opts) {
- var detatchInputEvents; // A function that detaches all events from the input
- var closing = false; // A hack to prevent calendar from re-opening when closing.
- var selectedDate; // The currently selected date
- var dp = {
- // The root DOM element for the date picker, initialized on first open.
- el: undefined,
- opts: opts,
- shouldFocusOnBlur: true,
- shouldFocusOnRender: true,
- state: initialState(),
- adjustPosition: noop,
- containerHTML: '',
+function propagateAllListeners(promise) {
+ //debug && linkToPreviousPromise(promise);
+ var listeners = promise._listeners;
+ promise._listeners = [];
- attachToDom: function () {
- document.body.appendChild(dp.el);
- },
+ for (var i = 0, len = listeners.length; i < len; ++i) {
+ propagateToListener(promise, listeners[i]);
+ }
- updateInput: function (selectedDate) {
- var e = new CustomEvent('change', {bubbles: true});
- e.simulated = true;
- input.value = selectedDate ? opts.format(selectedDate) : '';
- input.dispatchEvent(e);
- },
+ var psd = promise._PSD;
+ --psd.ref || psd.finalize(); // if psd.ref reaches zero, call psd.finalize();
- computeSelectedDate: function () {
- return opts.parse(input.value);
- },
+ if (numScheduledCalls === 0) {
+ // If numScheduledCalls is 0, it means that our stack is not in a callback of a scheduled call,
+ // and that no deferreds where listening to this rejection or success.
+ // Since there is a risk that our stack can contain application code that may
+ // do stuff after this code is finished that may generate new calls, we cannot
+ // call finalizers here.
+ ++numScheduledCalls;
+ asap$1(function () {
+ if (--numScheduledCalls === 0) finalizePhysicalTick(); // Will detect unhandled errors
+ }, []);
+ }
+}
- currentView: function() {
- return views[dp.state.view];
- },
+function propagateToListener(promise, listener) {
+ if (promise._state === null) {
+ promise._listeners.push(listener);
- open: function () {
- if (closing) {
- return;
- }
+ return;
+ }
- if (!dp.el) {
- dp.el = createContainerElement(opts, dp.containerHTML);
- attachContainerEvents(dp);
- }
+ var cb = promise._state ? listener.onFulfilled : listener.onRejected;
- selectedDate = constrainDate(dp.computeSelectedDate(), opts.min, opts.max);
- dp.state.hilightedDate = selectedDate || opts.hilightedDate;
- dp.state.view = 'day';
+ if (cb === null) {
+ // This Listener doesnt have a listener for the event being triggered (onFulfilled or onReject) so lets forward the event to any eventual listeners on the Promise instance returned by then() or catch()
+ return (promise._state ? listener.resolve : listener.reject)(promise._value);
+ }
- dp.attachToDom();
- dp.render();
+ ++listener.psd.ref;
+ ++numScheduledCalls;
+ asap$1(callListener, [cb, promise, listener]);
+}
- emit('open');
- },
+function callListener(cb, promise, listener) {
+ try {
+ // Set static variable currentFulfiller to the promise that is being fullfilled,
+ // so that we connect the chain of promises (for long stacks support)
+ currentFulfiller = promise; // Call callback and resolve our listener with it's return value.
- isVisible: function () {
- return !!dp.el && !!dp.el.parentNode;
- },
+ var ret,
+ value = promise._value;
- hasFocus: function () {
- var focused = document.activeElement;
- return dp.el &&
- dp.el.contains(focused) &&
- focused.className.indexOf('dp-focuser') < 0;
- },
+ if (promise._state) {
+ // cb is onResolved
+ ret = cb(value);
+ } else {
+ // cb is onRejected
+ if (rejectingErrors.length) rejectingErrors = [];
+ ret = cb(value);
+ if (rejectingErrors.indexOf(value) === -1) markErrorAsHandled(promise); // Callback didnt do Promise.reject(err) nor reject(err) onto another promise.
+ }
- shouldHide: function () {
- return dp.isVisible();
- },
+ listener.resolve(ret);
+ } catch (e) {
+ // Exception thrown in callback. Reject our listener.
+ listener.reject(e);
+ } finally {
+ // Restore env and currentFulfiller.
+ currentFulfiller = null;
+ if (--numScheduledCalls === 0) finalizePhysicalTick();
+ --listener.psd.ref || listener.psd.finalize();
+ }
+}
- close: function (becauseOfBlur) {
- var el = dp.el;
+function getStack(promise, stacks, limit) {
+ if (stacks.length === limit) return stacks;
+ var stack = "";
- if (!dp.isVisible()) {
- return;
- }
+ if (promise._state === false) {
+ var failure = promise._value,
+ errorName,
+ message;
- if (el) {
- var parent = el.parentNode;
- parent && parent.removeChild(el);
- }
+ if (failure != null) {
+ errorName = failure.name || "Error";
+ message = failure.message || failure;
+ stack = prettyStack(failure, 0);
+ } else {
+ errorName = failure; // If error is undefined or null, show that.
- closing = true;
+ message = "";
+ }
- if (becauseOfBlur && dp.shouldFocusOnBlur) {
- focusInput(input);
- }
+ stacks.push(errorName + (message ? ": " + message : "") + stack);
+ }
- // When we close, the input often gains refocus, which
- // can then launch the date picker again, so we buffer
- // a bit and don't show the date picker within N ms of closing
- setTimeout(function() {
- closing = false;
- }, 100);
+ if (debug) {
+ stack = prettyStack(promise._stackHolder, 2);
+ if (stack && stacks.indexOf(stack) === -1) stacks.push(stack);
+ if (promise._prev) getStack(promise._prev, stacks, limit);
+ }
- emit('close');
- },
+ return stacks;
+}
- destroy: function () {
- dp.close();
- detatchInputEvents();
- },
+function linkToPreviousPromise(promise, prev) {
+ // Support long stacks by linking to previous completed promise.
+ var numPrev = prev ? prev._numPrev + 1 : 0;
- render: function () {
- if (!dp.el) {
- return;
- }
+ if (numPrev < LONG_STACKS_CLIP_LIMIT) {
+ promise._prev = prev;
+ promise._numPrev = numPrev;
+ }
+}
+/* The callback to schedule with setImmediate() or setTimeout().
+ It runs a virtual microtick and executes any callback registered in microtickQueue.
+ */
- var hadFocus = dp.hasFocus();
- var html = dp.currentView().render(dp);
- html && (dp.el.firstChild.innerHTML = html);
- dp.adjustPosition();
+function physicalTick() {
+ beginMicroTickScope() && endMicroTickScope();
+}
- if (hadFocus || dp.shouldFocusOnRender) {
- focusCurrent(dp);
- }
- },
+function beginMicroTickScope() {
+ var wasRootExec = isOutsideMicroTick;
+ isOutsideMicroTick = false;
+ needsNewPhysicalTick = false;
+ return wasRootExec;
+}
+/* Executes micro-ticks without doing try..catch.
+ This can be possible because we only use this internally and
+ the registered functions are exception-safe (they do try..catch
+ internally before calling any external method). If registering
+ functions in the microtickQueue that are not exception-safe, this
+ would destroy the framework and make it instable. So we don't export
+ our asap method.
+*/
- // Conceptually similar to setState in React, updates
- // the view state and re-renders.
- setState: function (state) {
- for (var key in state) {
- dp.state[key] = state[key];
- }
- emit('statechange');
- dp.render();
- },
- };
+function endMicroTickScope() {
+ var callbacks, i, l;
- detatchInputEvents = attachInputEvents(input, dp);
+ do {
+ while (microtickQueue.length > 0) {
+ callbacks = microtickQueue;
+ microtickQueue = [];
+ l = callbacks.length;
- // Builds the initial view state
- // selectedDate is a special case and causes changes to hilightedDate
- // hilightedDate is set on open, so remains undefined initially
- // view is the current view (day, month, year)
- function initialState() {
- return {
- get selectedDate() {
- return selectedDate;
- },
- set selectedDate(dt) {
- if (dt && !opts.inRange(dt)) {
- return;
- }
+ for (i = 0; i < l; ++i) {
+ var item = callbacks[i];
+ item[0].apply(null, item[1]);
+ }
+ }
+ } while (microtickQueue.length > 0);
- if (dt) {
- selectedDate = new Date(dt);
- dp.state.hilightedDate = selectedDate;
- } else {
- selectedDate = dt;
- }
+ isOutsideMicroTick = true;
+ needsNewPhysicalTick = true;
+}
- dp.updateInput(selectedDate);
- emit('select');
- dp.close();
- },
- view: 'day',
- };
- }
+function finalizePhysicalTick() {
+ var unhandledErrs = unhandledErrors;
+ unhandledErrors = [];
+ unhandledErrs.forEach(function (p) {
+ p._PSD.onunhandled.call(null, p._value, p);
+ });
+ var finalizers = tickFinalizers.slice(0); // Clone first because finalizer may remove itself from list.
- return dp;
+ var i = finalizers.length;
+
+ while (i) {
+ finalizers[--i]();
}
+}
- function createContainerElement(opts, containerHTML) {
- var el = document.createElement('div');
+function run_at_end_of_this_or_next_physical_tick(fn) {
+ function finalizer() {
+ fn();
+ tickFinalizers.splice(tickFinalizers.indexOf(finalizer), 1);
+ }
- el.className = opts.mode;
- el.innerHTML = containerHTML;
+ tickFinalizers.push(finalizer);
+ ++numScheduledCalls;
+ asap$1(function () {
+ if (--numScheduledCalls === 0) finalizePhysicalTick();
+ }, []);
+}
- return el;
+function addPossiblyUnhandledError(promise) {
+ // Only add to unhandledErrors if not already there. The first one to add to this list
+ // will be upon the first rejection so that the root cause (first promise in the
+ // rejection chain) is the one listed.
+ if (!unhandledErrors.some(function (p) {
+ return p._value === promise._value;
+ })) unhandledErrors.push(promise);
+}
+
+function markErrorAsHandled(promise) {
+ // Called when a reject handled is actually being called.
+ // Search in unhandledErrors for any promise whos _value is this promise_value (list
+ // contains only rejected promises, and only one item per error)
+ var i = unhandledErrors.length;
+
+ while (i) {
+ if (unhandledErrors[--i]._value === promise._value) {
+ // Found a promise that failed with this same error object pointer,
+ // Remove that since there is a listener that actually takes care of it.
+ unhandledErrors.splice(i, 1);
+ return;
+ }
}
+}
- function attachInputEvents(input, dp) {
- var bufferShow = bufferFn(5, function () {
- if (dp.shouldHide()) {
- dp.close();
- } else {
- dp.open();
- }
- });
+function PromiseReject(reason) {
+ return new Promise(INTERNAL, false, reason);
+}
- var off = [
- on('blur', input, bufferFn(150, function () {
- if (!dp.hasFocus()) {
- dp.close(true);
- }
- })),
+function wrap(fn, errorCatcher) {
+ var psd = PSD;
+ return function () {
+ var wasRootExec = beginMicroTickScope(),
+ outerScope = PSD;
- on('mousedown', input, function () {
- if (input === document.activeElement) {
- bufferShow();
- }
- }),
+ try {
+ switchToZone(psd, true);
+ return fn.apply(this, arguments);
+ } catch (e) {
+ errorCatcher && errorCatcher(e);
+ } finally {
+ switchToZone(outerScope, false);
+ if (wasRootExec) endMicroTickScope();
+ }
+ };
+} //
+// variables used for native await support
+//
- on('focus', input, bufferShow),
- on('input', input, function (e) {
- var date = dp.opts.parse(e.target.value);
- isNaN(date) || dp.setState({
- hilightedDate: date
- });
- }),
- ];
+var task = {
+ awaits: 0,
+ echoes: 0,
+ id: 0
+}; // The ongoing macro-task when using zone-echoing.
- // Unregister all events that were registered above.
- return function() {
- off.forEach(function (f) {
- f();
- });
- };
- }
+var taskCounter = 0; // ID counter for macro tasks.
- function focusCurrent(dp) {
- var current = dp.el.querySelector('.dp-current');
- return current && current.focus();
- }
+var zoneStack = []; // Stack of left zones to restore asynchronically.
- function attachContainerEvents(dp) {
- var el = dp.el;
- var calEl = el.querySelector('.dp');
+var zoneEchoes = 0; // zoneEchoes is a must in order to persist zones between native await expressions.
- // Hack to get iOS to show active CSS states
- el.ontouchstart = noop;
+var totalEchoes = 0; // ID counter for micro-tasks. Used to detect possible native await in our Promise.prototype.then.
- function onClick(e) {
- e.target.className.split(' ').forEach(function(evt) {
- var handler = dp.currentView().onClick[evt];
- handler && handler(e, dp);
- });
- }
+var zone_id_counter = 0;
- // The calender fires a blur event *every* time we redraw
- // this means we need to buffer the blur event to see if
- // it still has no focus after redrawing, and only then
- // do we return focus to the input. A possible other approach
- // would be to set context.redrawing = true on redraw and
- // set it to false in the blur event.
- on('blur', calEl, bufferFn(150, function () {
- if (!dp.hasFocus()) {
- dp.close(true);
- }
- }));
+function newScope(fn, props$$1, a1, a2) {
+ var parent = PSD,
+ psd = Object.create(parent);
+ psd.parent = parent;
+ psd.ref = 0;
+ psd.global = false;
+ psd.id = ++zone_id_counter; // Prepare for promise patching (done in usePSD):
- on('keydown', el, function (e) {
- if (e.keyCode === Key.enter) {
- onClick(e);
- } else {
- dp.currentView().onKeyDown(e, dp);
- }
- });
+ var globalEnv = globalPSD.env;
+ psd.env = patchGlobalPromise ? {
+ Promise: Promise,
+ PromiseProp: {
+ value: Promise,
+ configurable: true,
+ writable: true
+ },
+ all: Promise.all,
+ race: Promise.race,
+ resolve: Promise.resolve,
+ reject: Promise.reject,
+ nthen: getPatchedPromiseThen(globalEnv.nthen, psd),
+ gthen: getPatchedPromiseThen(globalEnv.gthen, psd) // global then
+
+ } : {};
+ if (props$$1) extend(psd, props$$1); // unhandleds and onunhandled should not be specifically set here.
+ // Leave them on parent prototype.
+ // unhandleds.push(err) will push to parent's prototype
+ // onunhandled() will call parents onunhandled (with this scope's this-pointer though!)
+
+ ++parent.ref;
+
+ psd.finalize = function () {
+ --this.parent.ref || this.parent.finalize();
+ };
- // If the user clicks in non-focusable space, but
- // still within the date picker, we don't want to
- // hide, so we need to hack some things...
- on('mousedown', calEl, function (e) {
- e.target.focus && e.target.focus(); // IE hack
- if (document.activeElement !== e.target) {
- e.preventDefault();
- focusCurrent(dp);
- }
+ var rv = usePSD(psd, fn, a1, a2);
+ if (psd.ref === 0) psd.finalize();
+ return rv;
+} // Function to call if scopeFunc returns NativePromise
+// Also for each NativePromise in the arguments to Promise.all()
+
+
+function incrementExpectedAwaits() {
+ if (!task.id) task.id = ++taskCounter;
+ ++task.awaits;
+ task.echoes += ZONE_ECHO_LIMIT;
+ return task.id;
+} // Function to call when 'then' calls back on a native promise where onAwaitExpected() had been called.
+// Also call this when a native await calls then method on a promise. In that case, don't supply
+// sourceTaskId because we already know it refers to current task.
+
+
+function decrementExpectedAwaits(sourceTaskId) {
+ if (!task.awaits || sourceTaskId && sourceTaskId !== task.id) return;
+ if (--task.awaits === 0) task.id = 0;
+ task.echoes = task.awaits * ZONE_ECHO_LIMIT; // Will reset echoes to 0 if awaits is 0.
+} // Call from Promise.all() and Promise.race()
+
+
+function onPossibleParallellAsync(possiblePromise) {
+ if (task.echoes && possiblePromise && possiblePromise.constructor === NativePromise) {
+ incrementExpectedAwaits();
+ return possiblePromise.then(function (x) {
+ decrementExpectedAwaits();
+ return x;
+ }, function (e) {
+ decrementExpectedAwaits();
+ return rejection(e);
});
-
- on('click', el, onClick);
}
- function focusInput(input) {
- // When the modal closes, we need to focus the original input so the
- // user can continue tabbing from where they left off.
- input.focus();
+ return possiblePromise;
+}
- // iOS zonks out if we don't blur the input, so...
- if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) {
- input.blur();
- }
- }
+function zoneEnterEcho(targetZone) {
+ ++totalEchoes;
- /**
- * @file Defines the modal date picker behavior.
- */
+ if (!task.echoes || --task.echoes === 0) {
+ task.echoes = task.id = 0; // Cancel zone echoing.
+ }
- function ModalMode(input, emit, opts) {
- var dp = BaseMode(input, emit, opts);
+ zoneStack.push(PSD);
+ switchToZone(targetZone, true);
+}
- // In modal mode, users really shouldn't be able to type in
- // the input, as all input is done via the calendar.
- input.readonly = true;
+function zoneLeaveEcho() {
+ var zone = zoneStack[zoneStack.length - 1];
+ zoneStack.pop();
+ switchToZone(zone, false);
+}
- // In modal mode, we need to know when the user has tabbed
- // off the end of the calendar, and set focus to the original
- // input. To do this, we add a special element to the DOM.
- // When the user tabs off the bottom of the calendar, they
- // will tab onto this element.
- dp.containerHTML += '.';
+function switchToZone(targetZone, bEnteringZone) {
+ var currentZone = PSD;
- return dp;
+ if (bEnteringZone ? task.echoes && (!zoneEchoes++ || targetZone !== PSD) : zoneEchoes && (! --zoneEchoes || targetZone !== PSD)) {
+ // Enter or leave zone asynchronically as well, so that tasks initiated during current tick
+ // will be surrounded by the zone when they are invoked.
+ enqueueNativeMicroTask(bEnteringZone ? zoneEnterEcho.bind(null, targetZone) : zoneLeaveEcho);
}
- /**
- * @file Defines the dropdown date picker behavior.
- */
+ if (targetZone === PSD) return;
+ PSD = targetZone; // The actual zone switch occurs at this line.
+ // Snapshot on every leave from global zone.
- function DropdownMode(input, emit, opts) {
- var dp = BaseMode(input, emit, opts);
+ if (currentZone === globalPSD) globalPSD.env = snapShot();
- dp.shouldFocusOnBlur = false;
+ if (patchGlobalPromise) {
+ // Let's patch the global and native Promises (may be same or may be different)
+ var GlobalPromise = globalPSD.env.Promise; // Swich environments (may be PSD-zone or the global zone. Both apply.)
- Object.defineProperty(dp, 'shouldFocusOnRender', {
- get: function() {
- return input !== document.activeElement;
- }
- });
+ var targetEnv = targetZone.env; // Change Promise.prototype.then for native and global Promise (they MAY differ on polyfilled environments, but both can be accessed)
+ // Must be done on each zone change because the patched method contains targetZone in its closure.
- dp.adjustPosition = function () {
- autoPosition(input, dp);
- };
+ nativePromiseProto.then = targetEnv.nthen;
+ GlobalPromise.prototype.then = targetEnv.gthen;
- return dp;
+ if (currentZone.global || targetZone.global) {
+ // Leaving or entering global zone. It's time to patch / restore global Promise.
+ // Set this Promise to window.Promise so that transiled async functions will work on Firefox, Safari and IE, as well as with Zonejs and angular.
+ Object.defineProperty(_global, 'Promise', targetEnv.PromiseProp); // Support Promise.all() etc to work indexedDB-safe also when people are including es6-promise as a module (they might
+ // not be accessing global.Promise but a local reference to it)
+
+ GlobalPromise.all = targetEnv.all;
+ GlobalPromise.race = targetEnv.race;
+ GlobalPromise.resolve = targetEnv.resolve;
+ GlobalPromise.reject = targetEnv.reject;
+ }
}
+}
- function autoPosition(input, dp) {
- var inputPos = input.getBoundingClientRect();
- var win = window;
+function snapShot() {
+ var GlobalPromise = _global.Promise;
+ return patchGlobalPromise ? {
+ Promise: GlobalPromise,
+ PromiseProp: Object.getOwnPropertyDescriptor(_global, "Promise"),
+ all: GlobalPromise.all,
+ race: GlobalPromise.race,
+ resolve: GlobalPromise.resolve,
+ reject: GlobalPromise.reject,
+ nthen: nativePromiseProto.then,
+ gthen: GlobalPromise.prototype.then
+ } : {};
+}
- adjustCalY(dp, inputPos, win);
- adjustCalX(dp, inputPos, win);
+function usePSD(psd, fn, a1, a2, a3) {
+ var outerScope = PSD;
- dp.el.style.visibility = '';
+ try {
+ switchToZone(psd, true);
+ return fn(a1, a2, a3);
+ } finally {
+ switchToZone(outerScope, false);
}
+}
- function adjustCalX(dp, inputPos, win) {
- var cal = dp.el;
- var scrollLeft = win.pageXOffset;
- var inputLeft = inputPos.left + scrollLeft;
- var maxRight = win.innerWidth + scrollLeft;
- var offsetWidth = cal.offsetWidth;
- var calRight = inputLeft + offsetWidth;
- var shiftedLeft = maxRight - offsetWidth;
- var left = calRight > maxRight && shiftedLeft > 0 ? shiftedLeft : inputLeft;
-
- cal.style.left = left + 'px';
- }
+function enqueueNativeMicroTask(job) {
+ //
+ // Precondition: nativePromiseThen !== undefined
+ //
+ nativePromiseThen.call(resolvedNativePromise, job);
+}
- function adjustCalY(dp, inputPos, win) {
- var cal = dp.el;
- var scrollTop = win.pageYOffset;
- var inputTop = scrollTop + inputPos.top;
- var calHeight = cal.offsetHeight;
- var belowTop = inputTop + inputPos.height + 8;
- var aboveTop = inputTop - calHeight - 8;
- var isAbove = (aboveTop > 0 && belowTop + calHeight > scrollTop + win.innerHeight);
- var top = isAbove ? aboveTop : belowTop;
+function nativeAwaitCompatibleWrap(fn, zone, possibleAwait) {
+ return typeof fn !== 'function' ? fn : function () {
+ var outerZone = PSD;
+ if (possibleAwait) incrementExpectedAwaits();
+ switchToZone(zone, true);
- if (cal.classList) {
- cal.classList.toggle('dp-is-above', isAbove);
- cal.classList.toggle('dp-is-below', !isAbove);
+ try {
+ return fn.apply(this, arguments);
+ } finally {
+ switchToZone(outerZone, false);
}
- cal.style.top = top + 'px';
- }
+ };
+}
- /**
- * @file Defines the permanent date picker behavior.
- */
+function getPatchedPromiseThen(origThen, zone) {
+ return function (onResolved, onRejected) {
+ return origThen.call(this, nativeAwaitCompatibleWrap(onResolved, zone, false), nativeAwaitCompatibleWrap(onRejected, zone, false));
+ };
+}
- function PermanentMode(root, emit, opts) {
- var dp = BaseMode(root, emit, opts);
+var UNHANDLEDREJECTION = "unhandledrejection";
- dp.close = noop;
- dp.destroy = noop;
- dp.updateInput = noop;
- dp.shouldFocusOnRender = opts.shouldFocusOnRender;
+function globalError(err, promise) {
+ var rv;
- dp.computeSelectedDate = function () {
- return opts.hilightedDate;
- };
+ try {
+ rv = promise.onuncatched(err);
+ } catch (e) {}
- dp.attachToDom = function () {
- root.appendChild(dp.el);
+ if (rv !== false) try {
+ var event,
+ eventData = {
+ promise: promise,
+ reason: err
};
- dp.open();
+ if (_global.document && document.createEvent) {
+ event = document.createEvent('Event');
+ event.initEvent(UNHANDLEDREJECTION, true, true);
+ extend(event, eventData);
+ } else if (_global.CustomEvent) {
+ event = new CustomEvent(UNHANDLEDREJECTION, {
+ detail: eventData
+ });
+ extend(event, eventData);
+ }
- return dp;
- }
+ if (event && _global.dispatchEvent) {
+ dispatchEvent(event);
+ if (!_global.PromiseRejectionEvent && _global.onunhandledrejection) // No native support for PromiseRejectionEvent but user has set window.onunhandledrejection. Manually call it.
+ try {
+ _global.onunhandledrejection(event);
+ } catch (_) {}
+ }
- /**
- * @file Defines the various date picker modes (modal, dropdown, permanent)
- */
+ if (!event.defaultPrevented) {
+ console.warn("Unhandled rejection: " + (err.stack || err));
+ }
+ } catch (e) {}
+}
- function Mode(input, emit, opts) {
- input = input && input.tagName ? input : document.querySelector(input);
+var rejection = Promise.reject;
- if (opts.mode === 'dp-modal') {
- return ModalMode(input, emit, opts);
- }
+function Events(ctx) {
+ var evs = {};
- if (opts.mode === 'dp-below') {
- return DropdownMode(input, emit, opts);
- }
+ var rv = function (eventName, subscriber) {
+ if (subscriber) {
+ // Subscribe. If additional arguments than just the subscriber was provided, forward them as well.
+ var i = arguments.length,
+ args = new Array(i - 1);
- if (opts.mode === 'dp-permanent') {
- return PermanentMode(input, emit, opts);
+ while (--i) {
+ args[i - 1] = arguments[i];
+ }
+
+ evs[eventName].subscribe.apply(null, args);
+ return ctx;
+ } else if (typeof eventName === 'string') {
+ // Return interface allowing to fire or unsubscribe from event
+ return evs[eventName];
}
- }
+ };
- /**
- * @file Defines simple event emitter behavior.
- */
+ rv.addEventType = add;
- /**
- * Emitter constructs a new emitter object which has on/off methods.
- *
- * @returns {EventEmitter}
- */
- function Emitter() {
- var handlers = {};
+ for (var i = 1, l = arguments.length; i < l; ++i) {
+ add(arguments[i]);
+ }
- function onOne(name, handler) {
- (handlers[name] = (handlers[name] || [])).push(handler);
- }
+ return rv;
- function onMany(fns) {
- for (var name in fns) {
- onOne(name, fns[name]);
+ function add(eventName, chainFunction, defaultFunction) {
+ if (_typeof(eventName) === 'object') return addConfiguredEvents(eventName);
+ if (!chainFunction) chainFunction = reverseStoppableEventChain;
+ if (!defaultFunction) defaultFunction = nop;
+ var context = {
+ subscribers: [],
+ fire: defaultFunction,
+ subscribe: function (cb) {
+ if (context.subscribers.indexOf(cb) === -1) {
+ context.subscribers.push(cb);
+ context.fire = chainFunction(context.fire, cb);
+ }
+ },
+ unsubscribe: function (cb) {
+ context.subscribers = context.subscribers.filter(function (fn) {
+ return fn !== cb;
+ });
+ context.fire = context.subscribers.reduce(chainFunction, defaultFunction);
}
- }
+ };
+ evs[eventName] = rv[eventName] = context;
+ return context;
+ }
- return {
- on: function (name, handler) {
- if (handler) {
- onOne(name, handler);
- } else {
- onMany(name);
- }
+ function addConfiguredEvents(cfg) {
+ // events(this, {reading: [functionChain, nop]});
+ keys(cfg).forEach(function (eventName) {
+ var args = cfg[eventName];
- return this;
- },
+ if (isArray(args)) {
+ add(eventName, cfg[eventName][0], cfg[eventName][1]);
+ } else if (args === 'asap') {
+ // Rather than approaching event subscription using a functional approach, we here do it in a for-loop where subscriber is executed in its own stack
+ // enabling that any exception that occur wont disturb the initiator and also not nescessary be catched and forgotten.
+ var context = add(eventName, mirror, function fire() {
+ // Optimazation-safe cloning of arguments into args.
+ var i = arguments.length,
+ args = new Array(i);
- emit: function (name, arg) {
- (handlers[name] || []).forEach(function (handler) {
- handler(name, arg);
- });
- },
+ while (i--) {
+ args[i] = arguments[i];
+ } // All each subscriber:
- off: function (name, handler) {
- if (!name) {
- handlers = {};
- } else if (!handler) {
- handlers[name] = [];
- } else {
- handlers[name] = (handlers[name] || []).filter(function (h) {
- return h !== handler;
+
+ context.subscribers.forEach(function (fn) {
+ asap(function fireEvent() {
+ fn.apply(null, args);
+ });
});
- }
+ });
+ } else throw new exceptions.InvalidArgument("Invalid event config");
+ });
+ }
+}
+/*
+ * Dexie.js - a minimalistic wrapper for IndexedDB
+ * ===============================================
+ *
+ * Copyright (c) 2014-2017 David Fahlander
+ *
+ * Version {version}, {date}
+ *
+ * http://dexie.org
+ *
+ * Apache License Version 2.0, January 2004, http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
- return this;
- }
- };
+
+var DEXIE_VERSION = '{version}';
+var maxString = String.fromCharCode(65535);
+
+var maxKey = function () {
+ try {
+ IDBKeyRange.only([[]]);
+ return [[]];
+ } catch (e) {
+ return maxString;
}
+}();
- /**
- * @file The root date picker file, defines public exports for the library.
- */
+var minKey = -Infinity;
+var INVALID_KEY_ARGUMENT = "Invalid key provided. Keys must be of type string, number, Date or Array.";
+var STRING_EXPECTED = "String expected.";
+var connections = [];
+var isIEOrEdge = typeof navigator !== 'undefined' && /(MSIE|Trident|Edge)/.test(navigator.userAgent);
+var hasIEDeleteObjectStoreBug = isIEOrEdge;
+var hangsOnDeleteLargeKeyRange = isIEOrEdge;
- /**
- * The date picker language configuration
- * @typedef {Object} LangOptions
- * @property {Array.} [days] - Days of the week
- * @property {Array.} [months] - Months of the year
- * @property {string} today - The label for the 'today' button
- * @property {string} close - The label for the 'close' button
- * @property {string} clear - The label for the 'clear' button
- */
+var dexieStackFrameFilter = function (frame) {
+ return !/(dexie\.js|dexie\.min\.js)/.test(frame);
+};
- /**
- * The configuration options for a date picker.
- *
- * @typedef {Object} DatePickerOptions
- * @property {LangOptions} [lang] - Configures the label text, defaults to English
- * @property {('dp-modal'|'dp-below'|'dp-permanent')} [mode] - The date picker mode, defaults to 'dp-modal'
- * @property {(string|Date)} [hilightedDate] - The date to hilight if no date is selected
- * @property {function(string|Date):Date} [parse] - Parses a date, the complement of the "format" function
- * @property {function(Date):string} [format] - Formats a date for displaying to user
- * @property {function(Date):string} [dateClass] - Associates a custom CSS class with a date
- * @property {function(Date):boolean} [inRange] - Indicates whether or not a date is selectable
- * @property {(string|Date)} [min] - The minimum selectable date (inclusive, default 100 years ago)
- * @property {(string|Date)} [max] - The maximum selectable date (inclusive, default 100 years from now)
- */
+var dbNamesDB; // Global database for backing Dexie.getDatabaseNames() on browser without indexedDB.webkitGetDatabaseNames()
+// Init debug
+
+setDebug(debug, dexieStackFrameFilter);
+
+function Dexie(dbName, options) {
+ /// Specify only if you wich to control which addons that should run on this instance
+ var deps = Dexie.dependencies;
+ var opts = extend({
+ // Default Options
+ addons: Dexie.addons,
+ autoOpen: true,
+ indexedDB: deps.indexedDB,
+ IDBKeyRange: deps.IDBKeyRange // Backend IDBKeyRange api. Default to browser env.
+
+ }, options);
+ var addons = opts.addons,
+ autoOpen = opts.autoOpen,
+ indexedDB = opts.indexedDB,
+ IDBKeyRange = opts.IDBKeyRange;
+ var globalSchema = this._dbSchema = {};
+ var versions = [];
+ var dbStoreNames = [];
+ var allTables = {}; ///
+
+ var idbdb = null; // Instance of IDBDatabase
+
+ var dbOpenError = null;
+ var isBeingOpened = false;
+ var onReadyBeingFired = null;
+ var openComplete = false;
+ var READONLY = "readonly",
+ READWRITE = "readwrite";
+ var db = this;
+ var dbReadyResolve,
+ dbReadyPromise = new Promise(function (resolve) {
+ dbReadyResolve = resolve;
+ }),
+ cancelOpen,
+ openCanceller = new Promise(function (_, reject) {
+ cancelOpen = reject;
+ });
+ var autoSchema = true;
+ var hasNativeGetDatabaseNames = !!getNativeGetDatabaseNamesFn(indexedDB),
+ hasGetAll;
+
+ function init() {
+ // Default subscribers to "versionchange" and "blocked".
+ // Can be overridden by custom handlers. If custom handlers return false, these default
+ // behaviours will be prevented.
+ db.on("versionchange", function (ev) {
+ // Default behavior for versionchange event is to close database connection.
+ // Caller can override this behavior by doing db.on("versionchange", function(){ return false; });
+ // Let's not block the other window from making it's delete() or open() call.
+ // NOTE! This event is never fired in IE,Edge or Safari.
+ if (ev.newVersion > 0) console.warn("Another connection wants to upgrade database '" + db.name + "'. Closing db now to resume the upgrade.");else console.warn("Another connection wants to delete database '" + db.name + "'. Closing db now to resume the delete request.");
+ db.close(); // In many web applications, it would be recommended to force window.reload()
+ // when this event occurs. To do that, subscribe to the versionchange event
+ // and call window.location.reload(true) if ev.newVersion > 0 (not a deletion)
+ // The reason for this is that your current web app obviously has old schema code that needs
+ // to be updated. Another window got a newer version of the app and needs to upgrade DB but
+ // your window is blocking it unless we close it here.
+ });
+ db.on("blocked", function (ev) {
+ if (!ev.newVersion || ev.newVersion < ev.oldVersion) console.warn("Dexie.delete('" + db.name + "') was blocked");else console.warn("Upgrade '" + db.name + "' blocked by other connection holding version " + ev.oldVersion / 10);
+ });
+ } //
+ //
+ //
+ // ------------------------- Versioning Framework---------------------------
+ //
+ //
+ //
- /**
- * The state values for the date picker
- *
- * @typedef {Object} DatePickerState
- * @property {string} view - The current view 'day' | 'month' | 'year'
- * @property {Date} selectedDate - The date which has been selected by the user
- * @property {Date} hilightedDate - The date which is currently hilighted / active
- */
- /**
- * An instance of TinyDatePicker
- *
- * @typedef {Object} DatePicker
- * @property {DatePickerState} state - The values currently displayed.
- * @property {function} on - Adds an event handler
- * @property {function} off - Removes an event handler
- * @property {function} setState - Changes the current state of the date picker
- * @property {function} open - Opens the date picker
- * @property {function} close - Closes the date picker
- * @property {function} destroy - Destroys the date picker (removing all handlers from the input, too)
- */
+ this.version = function (versionNumber) {
+ ///
+ ///
+ if (idbdb || isBeingOpened) throw new exceptions.Schema("Cannot add version when database is open");
+ this.verno = Math.max(this.verno, versionNumber);
+ var versionInstance = versions.filter(function (v) {
+ return v._cfg.version === versionNumber;
+ })[0];
+ if (versionInstance) return versionInstance;
+ versionInstance = new Version(versionNumber);
+ versions.push(versionInstance);
+ versions.sort(lowerVersionFirst); // Disable autoschema mode, as at least one version is specified.
- /**
- * TinyDatePicker constructs a new date picker for the specified input
- *
- * @param {HTMLElement | string} input The input or CSS selector associated with the datepicker
- * @param {DatePickerOptions} opts The options for initializing the date picker
- * @returns {DatePicker}
- */
- function TinyDatePicker(input, opts) {
- var emitter = Emitter();
- var options = DatePickerOptions(opts);
- var mode = Mode(input, emit, options);
- var me = {
- get state() {
- return mode.state;
- },
- on: emitter.on,
- off: emitter.off,
- setState: mode.setState,
- open: mode.open,
- close: mode.close,
- destroy: mode.destroy,
+ autoSchema = false;
+ return versionInstance;
+ };
+
+ function Version(versionNumber) {
+ this._cfg = {
+ version: versionNumber,
+ storesSource: null,
+ dbschema: {},
+ tables: {},
+ contentUpgrade: null
};
+ this.stores({}); // Derive earlier schemas by default.
+ }
+
+ extend(Version.prototype, {
+ stores: function (stores) {
+ ///
+ /// Defines the schema for a particular version
+ ///
+ ///
+ /// Example:
+ /// {users: "id++,first,last,&username,*email",
+ /// passwords: "id++,&username"}
+ ///
+ /// Syntax: {Table: "[primaryKey][++],[&][*]index1,[&][*]index2,..."}
+ /// Special characters:
+ /// "&" means unique key,
+ /// "*" means value is multiEntry,
+ /// "++" means auto-increment and only applicable for primary key
+ ///
+ this._cfg.storesSource = this._cfg.storesSource ? extend(this._cfg.storesSource, stores) : stores; // Derive stores from earlier versions if they are not explicitely specified as null or a new syntax.
+
+ var storesSpec = {};
+ versions.forEach(function (version) {
+ extend(storesSpec, version._cfg.storesSource);
+ });
+ var dbschema = this._cfg.dbschema = {};
- function emit(evt) {
- emitter.emit(evt, me);
+ this._parseStoresSpec(storesSpec, dbschema); // Update the latest schema to this version
+ // Update API
+
+
+ globalSchema = db._dbSchema = dbschema;
+ removeTablesApi([allTables, db, Transaction.prototype]); // Keep Transaction.prototype even though it should be depr.
+
+ setApiOnPlace([allTables, db, Transaction.prototype, this._cfg.tables], keys(dbschema), dbschema);
+ dbStoreNames = keys(dbschema);
+ return this;
+ },
+ upgrade: function (upgradeFunction) {
+ this._cfg.contentUpgrade = upgradeFunction;
+ return this;
+ },
+ _parseStoresSpec: function (stores, outSchema) {
+ keys(stores).forEach(function (tableName) {
+ if (stores[tableName] !== null) {
+ var instanceTemplate = {};
+ var indexes = parseIndexSyntax(stores[tableName]);
+ var primKey = indexes.shift();
+ if (primKey.multi) throw new exceptions.Schema("Primary key cannot be multi-valued");
+ if (primKey.keyPath) setByKeyPath(instanceTemplate, primKey.keyPath, primKey.auto ? 0 : primKey.keyPath);
+ indexes.forEach(function (idx) {
+ if (idx.auto) throw new exceptions.Schema("Only primary key can be marked as autoIncrement (++)");
+ if (!idx.keyPath) throw new exceptions.Schema("Index must have a name and cannot be an empty string");
+ setByKeyPath(instanceTemplate, idx.keyPath, idx.compound ? idx.keyPath.map(function () {
+ return "";
+ }) : "");
+ });
+ outSchema[tableName] = new TableSchema(tableName, primKey, indexes, instanceTemplate);
+ }
+ });
}
+ });
- return me;
- }
+ function runUpgraders(oldVersion, idbtrans, reject) {
+ var trans = db._createTransaction(READWRITE, dbStoreNames, globalSchema);
- return TinyDatePicker;
+ trans.create(idbtrans);
-})));
+ trans._completion.catch(reject);
-},{}],"../../../../brain/tools/utilities/DateUtils.js":[function(require,module,exports) {
-"use strict";
+ var rejectTransaction = trans._reject.bind(trans);
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
+ newScope(function () {
+ PSD.trans = trans;
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ if (oldVersion === 0) {
+ // Create tables:
+ keys(globalSchema).forEach(function (tableName) {
+ createTable(idbtrans, tableName, globalSchema[tableName].primKey, globalSchema[tableName].indexes);
+ });
+ Promise.follow(function () {
+ return db.on.populate.fire(trans);
+ }).catch(rejectTransaction);
+ } else updateTablesAndIndexes(oldVersion, trans, idbtrans).catch(rejectTransaction);
+ });
+ }
-function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
+ function updateTablesAndIndexes(oldVersion, trans, idbtrans) {
+ // Upgrade version to version, step-by-step from oldest to newest version.
+ // Each transaction object will contain the table set that was current in that version (but also not-yet-deleted tables from its previous version)
+ var queue = [];
+ var oldVersionStruct = versions.filter(function (version) {
+ return version._cfg.version === oldVersion;
+ })[0];
+ if (!oldVersionStruct) throw new exceptions.Upgrade("Dexie specification of currently installed DB version is missing");
+ globalSchema = db._dbSchema = oldVersionStruct._cfg.dbschema;
+ var anyContentUpgraderHasRun = false;
+ var versToRun = versions.filter(function (v) {
+ return v._cfg.version > oldVersion;
+ });
+ versToRun.forEach(function (version) {
+ ///
+ queue.push(function () {
+ var oldSchema = globalSchema;
+ var newSchema = version._cfg.dbschema;
+ adjustToExistingIndexNames(oldSchema, idbtrans);
+ adjustToExistingIndexNames(newSchema, idbtrans);
+ globalSchema = db._dbSchema = newSchema;
+ var diff = getSchemaDiff(oldSchema, newSchema); // Add tables
+
+ diff.add.forEach(function (tuple) {
+ createTable(idbtrans, tuple[0], tuple[1].primKey, tuple[1].indexes);
+ }); // Change tables
+
+ diff.change.forEach(function (change) {
+ if (change.recreate) {
+ throw new exceptions.Upgrade("Not yet support for changing primary key");
+ } else {
+ var store = idbtrans.objectStore(change.name); // Add indexes
-function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
+ change.add.forEach(function (idx) {
+ addIndex(store, idx);
+ }); // Update indexes
-var DateUtils =
-/*#__PURE__*/
-function () {
- //--------------------------
- // constructor
- //--------------------------
- function DateUtils() {
- _classCallCheck(this, DateUtils);
- } //--------------------------
- // methods
- //--------------------------
+ change.change.forEach(function (idx) {
+ store.deleteIndex(idx.name);
+ addIndex(store, idx);
+ }); // Delete indexes
+ change.del.forEach(function (idxName) {
+ store.deleteIndex(idxName);
+ });
+ }
+ });
- _createClass(DateUtils, [{
- key: "getMKtime",
- value: function getMKtime() {
- var time = new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate(), new Date().getHours(), new Date().getMinutes(), new Date().getSeconds(), 0).getTime() / 1000;
- return time;
- }
- }, {
- key: "convertMKtime",
- value: function convertMKtime(seconds) {
- var date = new Date(seconds * 1000);
- return date;
+ if (version._cfg.contentUpgrade) {
+ anyContentUpgraderHasRun = true;
+ return Promise.follow(function () {
+ version._cfg.contentUpgrade(trans);
+ });
+ }
+ });
+ queue.push(function (idbtrans) {
+ if (!anyContentUpgraderHasRun || !hasIEDeleteObjectStoreBug) {
+ var newSchema = version._cfg.dbschema; // Delete old tables
+
+ deleteRemovedTables(newSchema, idbtrans);
+ }
+ });
+ }); // Now, create a queue execution engine
+
+ function runQueue() {
+ return queue.length ? Promise.resolve(queue.shift()(trans.idbtrans)).then(runQueue) : Promise.resolve();
}
- }, {
- key: "getDate",
- value: function getDate(type, rawdate) {
- var day = rawdate != null || rawdate != '' ? String(new Date(rawdate).getUTCDate()) : String(new Date().getUTCDate());
- var month = rawdate != null || rawdate != '' ? String(new Date(rawdate).getUTCMonth() + 1) : String(new Date().getUTCMonth() + 1);
- var year = rawdate != null || rawdate != '' ? String(new Date(rawdate).getUTCFullYear()) : String(new Date().getUTCFullYear());
- var hour = rawdate != null || rawdate != '' ? String(new Date(rawdate).getUTCHours()) : String(new Date().getUTCHours());
- var minute = rawdate != null || rawdate != '' ? String(new Date(rawdate).getUTCMinutes()) : String(new Date().getUTCMinutes());
- var seconds = rawdate != null || rawdate != '' ? String(new Date(rawdate).getUTCSeconds()) : String(new Date().getUTCSeconds());
- var millisecond = rawdate != null || rawdate != '' ? String(new Date(rawdate).getUTCMilliseconds()) : String(new Date().getUTCMilliseconds());
- var offset = rawdate != null || rawdate != '' ? String(new Date(rawdate).getTimezoneOffset()) : String(new Date().getTimezoneOffset());
- if (day.length == 1) day = String("0" + day);
- if (month.length == 1) month = String("0" + month);
- offset = String(offset / 60);
- if (offset.length == 1) offset = String("0" + offset);
- switch (type) {
- case "day":
- return day;
- break;
+ return runQueue().then(function () {
+ createMissingTables(globalSchema, idbtrans); // At last, make sure to create any missing tables. (Needed by addons that add stores to DB without specifying version)
+ });
+ }
- case "month":
- return month;
- break;
+ function getSchemaDiff(oldSchema, newSchema) {
+ var diff = {
+ del: [],
+ add: [],
+ change: [] // Array of {name: tableName, recreate: newDefinition, del: delIndexNames, add: newIndexDefs, change: changedIndexDefs}
- case "year":
- return year;
- break;
+ };
- case "stamp":
- return String(year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + seconds + "." + millisecond + "-" + offset);
- break;
+ for (var table in oldSchema) {
+ if (!newSchema[table]) diff.del.push(table);
+ }
- default:
- return String(year + "-" + month + "-" + day);
- break;
- }
- } //--------------------------
- // event handlers
- //--------------------------
+ for (table in newSchema) {
+ var oldDef = oldSchema[table],
+ newDef = newSchema[table];
- }]);
+ if (!oldDef) {
+ diff.add.push([table, newDef]);
+ } else {
+ var change = {
+ name: table,
+ def: newDef,
+ recreate: false,
+ del: [],
+ add: [],
+ change: []
+ };
- return DateUtils;
-}();
+ if (oldDef.primKey.src !== newDef.primKey.src) {
+ // Primary key has changed. Remove and re-add table.
+ change.recreate = true;
+ diff.change.push(change);
+ } else {
+ // Same primary key. Just find out what differs:
+ var oldIndexes = oldDef.idxByName;
+ var newIndexes = newDef.idxByName;
-var _default = DateUtils;
-exports.default = _default;
-},{}],"../../../../node_modules/caret-pos/lib/esm2015/main.js":[function(require,module,exports) {
-"use strict";
+ for (var idxName in oldIndexes) {
+ if (!newIndexes[idxName]) change.del.push(idxName);
+ }
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.getOffset = exports.offset = exports.position = void 0;
-var attributes = ['borderBottomWidth', 'borderLeftWidth', 'borderRightWidth', 'borderTopStyle', 'borderRightStyle', 'borderBottomStyle', 'borderLeftStyle', 'borderTopWidth', 'boxSizing', 'fontFamily', 'fontSize', 'fontWeight', 'height', 'letterSpacing', 'lineHeight', 'marginBottom', 'marginLeft', 'marginRight', 'marginTop', 'outlineWidth', 'overflow', 'overflowX', 'overflowY', 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'textAlign', 'textOverflow', 'textTransform', 'whiteSpace', 'wordBreak', 'wordWrap'];
-/**
- * Create a mirror
- *
- * @param {Element} element The element
- * @param {string} html The html
- *
- * @return {object} The mirror object
- */
+ for (idxName in newIndexes) {
+ var oldIdx = oldIndexes[idxName],
+ newIdx = newIndexes[idxName];
+ if (!oldIdx) change.add.push(newIdx);else if (oldIdx.src !== newIdx.src) change.change.push(newIdx);
+ }
-var createMirror = function createMirror(element, html) {
- /**
- * The mirror element
- */
- var mirror = document.createElement('div');
- /**
- * Create the CSS for the mirror object
- *
- * @return {object} The style object
- */
+ if (change.del.length > 0 || change.add.length > 0 || change.change.length > 0) {
+ diff.change.push(change);
+ }
+ }
+ }
+ }
- var mirrorCss = function mirrorCss() {
- var css = {
- position: 'absolute',
- left: -9999,
- top: 0,
- zIndex: -2000
- };
+ return diff;
+ }
- if (element.tagName === 'TEXTAREA') {
- attributes.push('width');
- }
+ function createTable(idbtrans, tableName, primKey, indexes) {
+ ///
+ var store = idbtrans.db.createObjectStore(tableName, primKey.keyPath ? {
+ keyPath: primKey.keyPath,
+ autoIncrement: primKey.auto
+ } : {
+ autoIncrement: primKey.auto
+ });
+ indexes.forEach(function (idx) {
+ addIndex(store, idx);
+ });
+ return store;
+ }
- attributes.forEach(function (attr) {
- css[attr] = getComputedStyle(element)[attr];
+ function createMissingTables(newSchema, idbtrans) {
+ keys(newSchema).forEach(function (tableName) {
+ if (!idbtrans.db.objectStoreNames.contains(tableName)) {
+ createTable(idbtrans, tableName, newSchema[tableName].primKey, newSchema[tableName].indexes);
+ }
});
- return css;
- };
- /**
- * Initialize the mirror
- *
- * @param {string} html The html
- *
- * @return {void}
- */
+ }
+ function deleteRemovedTables(newSchema, idbtrans) {
+ for (var i = 0; i < idbtrans.db.objectStoreNames.length; ++i) {
+ var storeName = idbtrans.db.objectStoreNames[i];
- var initialize = function initialize(html) {
- var styles = mirrorCss();
- Object.keys(styles).forEach(function (key) {
- mirror.style[key] = styles[key];
+ if (newSchema[storeName] == null) {
+ idbtrans.db.deleteObjectStore(storeName);
+ }
+ }
+ }
+
+ function addIndex(store, idx) {
+ store.createIndex(idx.name, idx.keyPath, {
+ unique: idx.unique,
+ multiEntry: idx.multi
});
- mirror.innerHTML = html;
- element.parentNode.insertBefore(mirror, element.nextSibling);
- };
- /**
- * Get the rect
- *
- * @return {Rect} The bounding rect
- */
+ } //
+ //
+ // Dexie Protected API
+ //
+ //
- var rect = function rect() {
- var marker = mirror.ownerDocument.getElementById('caret-position-marker');
- var boundingRect = {
- left: marker.offsetLeft,
- top: marker.offsetTop,
- height: marker.offsetHeight
- };
- mirror.parentNode.removeChild(mirror);
- return boundingRect;
- };
+ this._allTables = allTables;
- initialize(html);
- return {
- rect: rect
+ this._createTransaction = function (mode, storeNames, dbschema, parentTransaction) {
+ return new Transaction(mode, storeNames, dbschema, parentTransaction);
};
-};
-
-var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
- return typeof obj;
-} : function (obj) {
- return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
-};
-/**
- * Check if a DOM Element is content editable
- *
- * @param {Element} element The DOM element
- *
- * @return {bool} If it is content editable
- */
+ /* Generate a temporary transaction when db operations are done outside a transaction scope.
+ */
-var isContentEditable = function isContentEditable(element) {
- return !!(element.contentEditable && element.contentEditable === 'true');
-};
-/**
- * Get the context from settings passed in
- *
- * @param {object} settings The settings object
- *
- * @return {object} window and document
- */
+ function tempTransaction(mode, storeNames, fn) {
+ if (!openComplete && !PSD.letThrough) {
+ if (!isBeingOpened) {
+ if (!autoOpen) return rejection(new exceptions.DatabaseClosed());
+ db.open().catch(nop); // Open in background. If if fails, it will be catched by the final promise anyway.
+ }
+ return dbReadyPromise.then(function () {
+ return tempTransaction(mode, storeNames, fn);
+ });
+ } else {
+ var trans = db._createTransaction(mode, storeNames, globalSchema);
-var getContext = function getContext() {
- var settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
- var customPos = settings.customPos,
- iframe = settings.iframe,
- noShadowCaret = settings.noShadowCaret;
+ try {
+ trans.create();
+ } catch (ex) {
+ return rejection(ex);
+ }
- if (iframe) {
- return {
- iframe: iframe,
- window: iframe.contentWindow,
- document: iframe.contentDocument || iframe.contentWindow.document,
- noShadowCaret: noShadowCaret,
- customPos: customPos
- };
+ return trans._promise(mode, function (resolve, reject) {
+ return newScope(function () {
+ PSD.trans = trans;
+ return fn(resolve, reject, trans);
+ });
+ }).then(function (result) {
+ // Instead of resolving value directly, wait with resolving it until transaction has completed.
+ // Otherwise the data would not be in the DB if requesting it in the then() operation.
+ // Specifically, to ensure that the following expression will work:
+ //
+ // db.friends.put({name: "Arne"}).then(function () {
+ // db.friends.where("name").equals("Arne").count(function(count) {
+ // assert (count === 1);
+ // });
+ // });
+ //
+ return trans._completion.then(function () {
+ return result;
+ });
+ });
+ /*.catch(err => { // Don't do this as of now. If would affect bulk- and modify methods in a way that could be more intuitive. But wait! Maybe change in next major.
+ trans._reject(err);
+ return rejection(err);
+ });*/
+ }
}
- return {
- window: window,
- document: document,
- noShadowCaret: noShadowCaret,
- customPos: customPos
- };
-};
-/**
- * Get the offset of an element
- *
- * @param {Element} element The DOM element
- * @param {object} ctx The context
- *
- * @return {object} top and left
- */
+ this._whenReady = function (fn) {
+ return openComplete || PSD.letThrough ? fn() : new Promise(function (resolve, reject) {
+ if (!isBeingOpened) {
+ if (!autoOpen) {
+ reject(new exceptions.DatabaseClosed());
+ return;
+ }
+ db.open().catch(nop); // Open in background. If if fails, it will be catched by the final promise anyway.
+ }
-var getOffset = function getOffset(element, ctx) {
- var win = ctx && ctx.window || window;
- var doc = ctx && ctx.document || document;
- var rect = element.getBoundingClientRect();
- var docEl = doc.documentElement;
- var scrollLeft = win.pageXOffset || docEl.scrollLeft;
- var scrollTop = win.pageYOffset || docEl.scrollTop;
- return {
- top: rect.top + scrollTop,
- left: rect.left + scrollLeft
- };
-};
-/**
- * Check if a value is an object
- *
- * @param {any} value The value to check
- *
- * @return {bool} If it is an object
- */
+ dbReadyPromise.then(resolve, reject);
+ }).then(fn);
+ }; //
+ //
+ //
+ //
+ // Dexie API
+ //
+ //
+ //
-exports.getOffset = getOffset;
+ this.verno = 0;
-var isObject = function isObject(value) {
- return (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value !== null;
-};
-/**
- * Create a Input caret object.
- *
- * @param {Element} element The element
- * @param {Object} ctx The context
- */
+ this.open = function () {
+ if (isBeingOpened || idbdb) return dbReadyPromise.then(function () {
+ return dbOpenError ? rejection(dbOpenError) : db;
+ });
+ debug && (openCanceller._stackHolder = getErrorWithStack()); // Let stacks point to when open() was called rather than where new Dexie() was called.
+
+ isBeingOpened = true;
+ dbOpenError = null;
+ openComplete = false; // Function pointers to call when the core opening process completes.
+
+ var resolveDbReady = dbReadyResolve,
+ // upgradeTransaction to abort on failure.
+ upgradeTransaction = null;
+ return Promise.race([openCanceller, new Promise(function (resolve, reject) {
+ // Multiply db.verno with 10 will be needed to workaround upgrading bug in IE:
+ // IE fails when deleting objectStore after reading from it.
+ // A future version of Dexie.js will stopover an intermediate version to workaround this.
+ // At that point, we want to be backward compatible. Could have been multiplied with 2, but by using 10, it is easier to map the number to the real version number.
+ // If no API, throw!
+ if (!indexedDB) throw new exceptions.MissingAPI("indexedDB API not found. If using IE10+, make sure to run your code on a server URL " + "(not locally). If using old Safari versions, make sure to include indexedDB polyfill.");
+ var req = autoSchema ? indexedDB.open(dbName) : indexedDB.open(dbName, Math.round(db.verno * 10));
+ if (!req) throw new exceptions.MissingAPI("IndexedDB API not available"); // May happen in Safari private mode, see https://github.com/dfahlander/Dexie.js/issues/134
+
+ req.onerror = eventRejectHandler(reject);
+ req.onblocked = wrap(fireOnBlocked);
+ req.onupgradeneeded = wrap(function (e) {
+ upgradeTransaction = req.transaction;
+
+ if (autoSchema && !db._allowEmptyDB) {
+ // Caller did not specify a version or schema. Doing that is only acceptable for opening alread existing databases.
+ // If onupgradeneeded is called it means database did not exist. Reject the open() promise and make sure that we
+ // do not create a new database by accident here.
+ req.onerror = preventDefault; // Prohibit onabort error from firing before we're done!
+
+ upgradeTransaction.abort(); // Abort transaction (would hope that this would make DB disappear but it doesnt.)
+ // Close database and delete it.
+
+ req.result.close();
+ var delreq = indexedDB.deleteDatabase(dbName); // The upgrade transaction is atomic, and javascript is single threaded - meaning that there is no risk that we delete someone elses database here!
+
+ delreq.onsuccess = delreq.onerror = wrap(function () {
+ reject(new exceptions.NoSuchDatabase("Database " + dbName + " doesnt exist"));
+ });
+ } else {
+ upgradeTransaction.onerror = eventRejectHandler(reject);
+ var oldVer = e.oldVersion > Math.pow(2, 62) ? 0 : e.oldVersion; // Safari 8 fix.
+
+ runUpgraders(oldVer / 10, upgradeTransaction, reject, req);
+ }
+ }, reject);
+ req.onsuccess = wrap(function () {
+ // Core opening procedure complete. Now let's just record some stuff.
+ upgradeTransaction = null;
+ idbdb = req.result;
+ connections.push(db); // Used for emulating versionchange event on IE/Edge/Safari.
+
+ if (autoSchema) readGlobalSchema();else if (idbdb.objectStoreNames.length > 0) {
+ try {
+ adjustToExistingIndexNames(globalSchema, idbdb.transaction(safariMultiStoreFix(idbdb.objectStoreNames), READONLY));
+ } catch (e) {// Safari may bail out if > 1 store names. However, this shouldnt be a showstopper. Issue #120.
+ }
+ }
+ idbdb.onversionchange = wrap(function (ev) {
+ db._vcFired = true; // detect implementations that not support versionchange (IE/Edge/Safari)
+ db.on("versionchange").fire(ev);
+ });
-var createInputCaret = function createInputCaret(element, ctx) {
- /**
- * Get the current position
- *
- * @returns {int} The caret position
- */
- var getPos = function getPos() {
- return element.selectionStart;
- };
- /**
- * Set the position
- *
- * @param {int} pos The position
- *
- * @return {Element} The element
- */
+ if (!hasNativeGetDatabaseNames && dbName !== '__dbnames') {
+ dbNamesDB.dbnames.put({
+ name: dbName
+ }).catch(nop);
+ }
+
+ resolve();
+ }, reject);
+ })]).then(function () {
+ // Before finally resolving the dbReadyPromise and this promise,
+ // call and await all on('ready') subscribers:
+ // Dexie.vip() makes subscribers able to use the database while being opened.
+ // This is a must since these subscribers take part of the opening procedure.
+ onReadyBeingFired = [];
+ return Promise.resolve(Dexie.vip(db.on.ready.fire)).then(function fireRemainders() {
+ if (onReadyBeingFired.length > 0) {
+ // In case additional subscribers to db.on('ready') were added during the time db.on.ready.fire was executed.
+ var remainders = onReadyBeingFired.reduce(promisableChain, nop);
+ onReadyBeingFired = [];
+ return Promise.resolve(Dexie.vip(remainders)).then(fireRemainders);
+ }
+ });
+ }).finally(function () {
+ onReadyBeingFired = null;
+ }).then(function () {
+ // Resolve the db.open() with the db instance.
+ isBeingOpened = false;
+ return db;
+ }).catch(function (err) {
+ try {
+ // Did we fail within onupgradeneeded? Make sure to abort the upgrade transaction so it doesnt commit.
+ upgradeTransaction && upgradeTransaction.abort();
+ } catch (e) {}
+ isBeingOpened = false; // Set before calling db.close() so that it doesnt reject openCanceller again (leads to unhandled rejection event).
- var setPos = function setPos(pos) {
- element.setSelectionRange(pos, pos);
- return element;
- };
- /**
- * The offset
- *
- * @param {int} pos The position
- *
- * @return {object} The offset
- */
+ db.close(); // Closes and resets idbdb, removes connections, resets dbReadyPromise and openCanceller so that a later db.open() is fresh.
+ // A call to db.close() may have made on-ready subscribers fail. Use dbOpenError if set, since err could be a follow-up error on that.
+ dbOpenError = err; // Record the error. It will be used to reject further promises of db operations.
- var getOffset$$1 = function getOffset$$1(pos) {
- var rect = getOffset(element);
- var position = getPosition(pos);
- return {
- top: rect.top + position.top + ctx.document.body.scrollTop,
- left: rect.left + position.left + ctx.document.body.scrollLeft,
- height: position.height
- };
+ return rejection(dbOpenError);
+ }).finally(function () {
+ openComplete = true;
+ resolveDbReady(); // dbReadyPromise is resolved no matter if open() rejects or resolved. It's just to wake up waiters.
+ });
};
- /**
- * Get the current position
- *
- * @param {int} pos The position
- *
- * @return {object} The position
- */
+ this.close = function () {
+ var idx = connections.indexOf(db);
+ if (idx >= 0) connections.splice(idx, 1);
- var getPosition = function getPosition(pos) {
- var format = function format(val) {
- var value = val.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g, ' ');
- return value;
- };
+ if (idbdb) {
+ try {
+ idbdb.close();
+ } catch (e) {}
- if (ctx.customPos || ctx.customPos === 0) {
- pos = ctx.customPos;
+ idbdb = null;
}
- var position = pos === undefined ? getPos() : pos;
- var startRange = element.value.slice(0, position);
- var endRange = element.value.slice(position);
- var html = '' + format(startRange) + '';
- html += '|';
- html += '' + format(endRange) + '';
- var mirror = createMirror(element, html);
- var rect = mirror.rect();
- rect.pos = getPos();
- return rect;
- };
+ autoOpen = false;
+ dbOpenError = new exceptions.DatabaseClosed();
+ if (isBeingOpened) cancelOpen(dbOpenError); // Reset dbReadyPromise promise:
- return {
- getPos: getPos,
- setPos: setPos,
- getOffset: getOffset$$1,
- getPosition: getPosition
+ dbReadyPromise = new Promise(function (resolve) {
+ dbReadyResolve = resolve;
+ });
+ openCanceller = new Promise(function (_, reject) {
+ cancelOpen = reject;
+ });
};
-};
-/**
- * Create an Editable Caret
- * @param {Element} element The editable element
- * @param {object|null} ctx The context
- *
- * @return {EditableCaret}
- */
+ this.delete = function () {
+ var hasArguments = arguments.length > 0;
+ return new Promise(function (resolve, reject) {
+ if (hasArguments) throw new exceptions.InvalidArgument("Arguments not allowed in db.delete()");
-var createEditableCaret = function createEditableCaret(element, ctx) {
- /**
- * Set the caret position
- *
- * @param {int} pos The position to se
- *
- * @return {Element} The element
- */
- var setPos = function setPos(pos) {
- var sel = ctx.window.getSelection();
+ if (isBeingOpened) {
+ dbReadyPromise.then(doDelete);
+ } else {
+ doDelete();
+ }
- if (sel) {
- var offset = 0;
- var found = false;
+ function doDelete() {
+ db.close();
+ var req = indexedDB.deleteDatabase(dbName);
+ req.onsuccess = wrap(function () {
+ if (!hasNativeGetDatabaseNames) {
+ dbNamesDB.dbnames.delete(dbName).catch(nop);
+ }
- var find = function find(position, parent) {
- for (var i = 0; i < parent.childNodes.length; i++) {
- var node = parent.childNodes[i];
+ resolve();
+ });
+ req.onerror = eventRejectHandler(reject);
+ req.onblocked = fireOnBlocked;
+ }
+ });
+ };
- if (found) {
- break;
- }
+ this.backendDB = function () {
+ return idbdb;
+ };
- if (node.nodeType === 3) {
- if (offset + node.length >= position) {
- found = true;
- var range = ctx.document.createRange();
- range.setStart(node, position - offset);
- sel.removeAllRanges();
- sel.addRange(range);
- break;
- } else {
- offset += node.length;
- }
- } else {
- find(pos, node);
- }
- }
- };
+ this.isOpen = function () {
+ return idbdb !== null;
+ };
- find(pos, element);
- }
+ this.hasBeenClosed = function () {
+ return dbOpenError && dbOpenError instanceof exceptions.DatabaseClosed;
+ };
- return element;
+ this.hasFailed = function () {
+ return dbOpenError !== null;
};
- /**
- * Get the offset
- *
- * @return {object} The offset
- */
+ this.dynamicallyOpened = function () {
+ return autoSchema;
+ }; //
+ // Properties
+ //
- var getOffset = function getOffset() {
- var range = getRange();
- var offset = {
- height: 0,
- left: 0,
- right: 0
- };
- if (!range) {
- return offset;
+ this.name = dbName; // db.tables - an array of all Table instances.
+
+ props(this, {
+ tables: {
+ get: function () {
+ ///
+ return keys(allTables).map(function (name) {
+ return allTables[name];
+ });
+ }
}
+ }); //
+ // Events
+ //
- var hasCustomPos = ctx.customPos || ctx.customPos === 0; // endContainer in Firefox would be the element at the start of
- // the line
+ this.on = Events(this, "populate", "blocked", "versionchange", {
+ ready: [promisableChain, nop]
+ });
+ this.on.ready.subscribe = override(this.on.ready.subscribe, function (subscribe) {
+ return function (subscriber, bSticky) {
+ Dexie.vip(function () {
+ if (openComplete) {
+ // Database already open. Call subscriber asap.
+ if (!dbOpenError) Promise.resolve().then(subscriber); // bSticky: Also subscribe to future open sucesses (after close / reopen)
+
+ if (bSticky) subscribe(subscriber);
+ } else if (onReadyBeingFired) {
+ // db.on('ready') subscribers are currently being executed and have not yet resolved or rejected
+ onReadyBeingFired.push(subscriber);
+ if (bSticky) subscribe(subscriber);
+ } else {
+ // Database not yet open. Subscribe to it.
+ subscribe(subscriber); // If bSticky is falsy, make sure to unsubscribe subscriber when fired once.
- if (range.endOffset - 1 > 0 && range.endContainer !== element || hasCustomPos) {
- var clonedRange = range.cloneRange();
- var fixedPosition = hasCustomPos ? ctx.customPos : range.endOffset;
- clonedRange.setStart(range.endContainer, fixedPosition - 1 < 0 ? 0 : fixedPosition - 1);
- clonedRange.setEnd(range.endContainer, fixedPosition);
- var rect = clonedRange.getBoundingClientRect();
- offset = {
- height: rect.height,
- left: rect.left + rect.width,
- top: rect.top
- };
- clonedRange.detach();
- }
+ if (!bSticky) subscribe(function unsubscribe() {
+ db.on.ready.unsubscribe(subscriber);
+ db.on.ready.unsubscribe(unsubscribe);
+ });
+ }
+ });
+ };
+ });
- if ((!offset || offset && offset.height === 0) && !ctx.noShadowCaret) {
- var _clonedRange = range.cloneRange();
+ this.transaction = function () {
+ ///
+ ///
+ ///
+ /// "r" for readonly, or "rw" for readwrite
+ /// Table instance, Array of Table instances, String or String Array of object stores to include in the transaction
+ /// Function to execute with transaction
+ var args = extractTransactionArgs.apply(this, arguments);
+ return this._transaction.apply(this, args);
+ };
- var shadowCaret = ctx.document.createTextNode('|');
+ function extractTransactionArgs(mode, _tableArgs_, scopeFunc) {
+ // Let table arguments be all arguments between mode and last argument.
+ var i = arguments.length;
+ if (i < 2) throw new exceptions.InvalidArgument("Too few arguments"); // Prevent optimzation killer (https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments)
+ // and clone arguments except the first one into local var 'args'.
- _clonedRange.insertNode(shadowCaret);
+ var args = new Array(i - 1);
- _clonedRange.selectNode(shadowCaret);
+ while (--i) {
+ args[i - 1] = arguments[i];
+ } // Let scopeFunc be the last argument and pop it so that args now only contain the table arguments.
- var _rect = _clonedRange.getBoundingClientRect();
- offset = {
- height: _rect.height,
- left: _rect.left,
- top: _rect.top
- };
- shadowCaret.parentNode.removeChild(shadowCaret);
+ scopeFunc = args.pop();
+ var tables = flatten(args); // Support using array as middle argument, or a mix of arrays and non-arrays.
- _clonedRange.detach();
- }
+ return [mode, tables, scopeFunc];
+ }
- if (offset) {
- var doc = ctx.document.documentElement;
- offset.top += ctx.window.pageYOffset - (doc.clientTop || 0);
- offset.left += ctx.window.pageXOffset - (doc.clientLeft || 0);
- }
+ this._transaction = function (mode, tables, scopeFunc) {
+ var parentTransaction = PSD.trans; // Check if parent transactions is bound to this db instance, and if caller wants to reuse it
- return offset;
- };
- /**
- * Get the position
- *
- * @return {object} The position
- */
+ if (!parentTransaction || parentTransaction.db !== db || mode.indexOf('!') !== -1) parentTransaction = null;
+ var onlyIfCompatible = mode.indexOf('?') !== -1;
+ mode = mode.replace('!', '').replace('?', ''); // Ok. Will change arguments[0] as well but we wont touch arguments henceforth.
+ try {
+ //
+ // Get storeNames from arguments. Either through given table instances, or through given table names.
+ //
+ var storeNames = tables.map(function (table) {
+ var storeName = table instanceof Table ? table.name : table;
+ if (typeof storeName !== 'string') throw new TypeError("Invalid table argument to Dexie.transaction(). Only Table or String are allowed");
+ return storeName;
+ }); //
+ // Resolve mode. Allow shortcuts "r" and "rw".
+ //
+
+ if (mode == "r" || mode == READONLY) mode = READONLY;else if (mode == "rw" || mode == READWRITE) mode = READWRITE;else throw new exceptions.InvalidArgument("Invalid transaction mode: " + mode);
+
+ if (parentTransaction) {
+ // Basic checks
+ if (parentTransaction.mode === READONLY && mode === READWRITE) {
+ if (onlyIfCompatible) {
+ // Spawn new transaction instead.
+ parentTransaction = null;
+ } else throw new exceptions.SubTransaction("Cannot enter a sub-transaction with READWRITE mode when parent transaction is READONLY");
+ }
+
+ if (parentTransaction) {
+ storeNames.forEach(function (storeName) {
+ if (parentTransaction && parentTransaction.storeNames.indexOf(storeName) === -1) {
+ if (onlyIfCompatible) {
+ // Spawn new transaction instead.
+ parentTransaction = null;
+ } else throw new exceptions.SubTransaction("Table " + storeName + " not included in parent transaction.");
+ }
+ });
+ }
- var getPosition = function getPosition() {
- var offset = getOffset();
- var pos = getPos();
- var rect = element.getBoundingClientRect();
- var inputOffset = {
- top: rect.top + ctx.document.body.scrollTop,
- left: rect.left + ctx.document.body.scrollLeft
- };
- offset.left -= inputOffset.left;
- offset.top -= inputOffset.top;
- offset.pos = pos;
- return offset;
- };
- /**
- * Get the range
- *
- * @return {Range|null}
- */
+ if (onlyIfCompatible && parentTransaction && !parentTransaction.active) {
+ // '?' mode should not keep using an inactive transaction.
+ parentTransaction = null;
+ }
+ }
+ } catch (e) {
+ return parentTransaction ? parentTransaction._promise(null, function (_, reject) {
+ reject(e);
+ }) : rejection(e);
+ } // If this is a sub-transaction, lock the parent and then launch the sub-transaction.
- var getRange = function getRange() {
- if (!ctx.window.getSelection) {
- return;
- }
+ return parentTransaction ? parentTransaction._promise(mode, enterTransactionScope, "lock") : PSD.trans ? // no parent transaction despite PSD.trans exists. Make sure also
+ // that the zone we create is not a sub-zone of current, because
+ // Promise.follow() should not wait for it if so.
+ usePSD(PSD.transless, function () {
+ return db._whenReady(enterTransactionScope);
+ }) : db._whenReady(enterTransactionScope);
- var sel = ctx.window.getSelection();
- return sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
- };
- /**
- * Get the caret position
- *
- * @return {int} The position
- */
+ function enterTransactionScope() {
+ return Promise.resolve().then(function () {
+ // Keep a pointer to last non-transactional PSD to use if someone calls Dexie.ignoreTransaction().
+ var transless = PSD.transless || PSD; // Our transaction.
+ //return new Promise((resolve, reject) => {
+ var trans = db._createTransaction(mode, storeNames, globalSchema, parentTransaction); // Let the transaction instance be part of a Promise-specific data (PSD) value.
- var getPos = function getPos() {
- var range = getRange();
- var clonedRange = range.cloneRange();
- clonedRange.selectNodeContents(element);
- clonedRange.setEnd(range.endContainer, range.endOffset);
- var pos = clonedRange.toString().length;
- clonedRange.detach();
- return pos;
- };
- return {
- getPos: getPos,
- setPos: setPos,
- getPosition: getPosition,
- getOffset: getOffset,
- getRange: getRange
- };
-};
+ var zoneProps = {
+ trans: trans,
+ transless: transless
+ };
-var createCaret = function createCaret(element, ctx) {
- if (isContentEditable(element)) {
- return createEditableCaret(element, ctx);
- }
+ if (parentTransaction) {
+ // Emulate transaction commit awareness for inner transaction (must 'commit' when the inner transaction has no more operations ongoing)
+ trans.idbtrans = parentTransaction.idbtrans;
+ } else {
+ trans.create(); // Create the backend transaction so that complete() or error() will trigger even if no operation is made upon it.
+ } // Support for native async await.
- return createInputCaret(element, ctx);
-};
-var position = function position(element, value) {
- var settings = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
- var options = settings;
+ if (scopeFunc.constructor === AsyncFunction) {
+ incrementExpectedAwaits();
+ }
- if (isObject(value)) {
- options = value;
- value = null;
- }
+ var returnValue;
+ var promiseFollowed = Promise.follow(function () {
+ // Finally, call the scope function with our table and transaction arguments.
+ returnValue = scopeFunc.call(trans, trans);
- var ctx = getContext(options);
- var caret = createCaret(element, ctx);
+ if (returnValue) {
+ if (returnValue.constructor === NativePromise) {
+ var decrementor = decrementExpectedAwaits.bind(null, null);
+ returnValue.then(decrementor, decrementor);
+ } else if (typeof returnValue.next === 'function' && typeof returnValue.throw === 'function') {
+ // scopeFunc returned an iterator with throw-support. Handle yield as await.
+ returnValue = awaitIterator(returnValue);
+ }
+ }
+ }, zoneProps);
+ return (returnValue && typeof returnValue.then === 'function' ? // Promise returned. User uses promise-style transactions.
+ Promise.resolve(returnValue).then(function (x) {
+ return trans.active ? x // Transaction still active. Continue.
+ : rejection(new exceptions.PrematureCommit("Transaction committed too early. See http://bit.ly/2kdckMn"));
+ }) // No promise returned. Wait for all outstanding promises before continuing.
+ : promiseFollowed.then(function () {
+ return returnValue;
+ })).then(function (x) {
+ // sub transactions don't react to idbtrans.oncomplete. We must trigger a completion:
+ if (parentTransaction) trans._resolve(); // wait for trans._completion
+ // (if root transaction, this means 'complete' event. If sub-transaction, we've just fired it ourselves)
+
+ return trans._completion.then(function () {
+ return x;
+ });
+ }).catch(function (e) {
+ trans._reject(e); // Yes, above then-handler were maybe not called because of an unhandled rejection in scopeFunc!
- if (value || value === 0) {
- return caret.setPos(value);
- }
- return caret.getPosition();
-};
-/**
- *
- * @param {Element} element The DOM element
- * @param {number|undefined} value The value to set
- * @param {object} settings Any settings for context
- */
+ return rejection(e);
+ });
+ });
+ }
+ };
+ this.table = function (tableName) {
+ ///
+ if (!hasOwn(allTables, tableName)) {
+ throw new exceptions.InvalidTable("Table " + tableName + " does not exist");
+ }
-exports.position = position;
+ return allTables[tableName];
+ }; //
+ //
+ //
+ // Table Class
+ //
+ //
+ //
-var offset = function offset(element, value) {
- var settings = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
- var options = settings;
- if (isObject(value)) {
- options = value;
- value = null;
+ function Table(name, tableSchema, optionalTrans) {
+ ///
+ this.name = name;
+ this.schema = tableSchema;
+ this._tx = optionalTrans;
+ this.hook = allTables[name] ? allTables[name].hook : Events(null, {
+ "creating": [hookCreatingChain, nop],
+ "reading": [pureFunctionChain, mirror],
+ "updating": [hookUpdatingChain, nop],
+ "deleting": [hookDeletingChain, nop]
+ });
}
- var ctx = getContext(options);
- var caret = createCaret(element, ctx);
- return caret.getOffset(value);
-};
+ function BulkErrorHandlerCatchAll(errorList, done, supportHooks) {
+ return (supportHooks ? hookedEventRejectHandler : eventRejectHandler)(function (e) {
+ errorList.push(e);
+ done && done();
+ });
+ }
-exports.offset = offset;
-},{}],"../../../../brain/tools/ui/TextEditor.js":[function(require,module,exports) {
-"use strict";
-
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
-
-var DataEvent = _interopRequireWildcard(require("../events/DataEvent"));
+ function bulkDelete(idbstore, trans, keysOrTuples, hasDeleteHook, deletingHook) {
+ // If hasDeleteHook, keysOrTuples must be an array of tuples: [[key1, value2],[key2,value2],...],
+ // else keysOrTuples must be just an array of keys: [key1, key2, ...].
+ return new Promise(function (resolve, reject) {
+ var len = keysOrTuples.length,
+ lastItem = len - 1;
+ if (len === 0) return resolve();
+
+ if (!hasDeleteHook) {
+ for (var i = 0; i < len; ++i) {
+ var req = idbstore.delete(keysOrTuples[i]);
+ req.onerror = eventRejectHandler(reject);
+ if (i === lastItem) req.onsuccess = wrap(function () {
+ return resolve();
+ });
+ }
+ } else {
+ var hookCtx,
+ errorHandler = hookedEventRejectHandler(reject),
+ successHandler = hookedEventSuccessHandler(null);
+ tryCatch(function () {
+ for (var i = 0; i < len; ++i) {
+ hookCtx = {
+ onsuccess: null,
+ onerror: null
+ };
+ var tuple = keysOrTuples[i];
+ deletingHook.call(hookCtx, tuple[0], tuple[1], trans);
+ var req = idbstore.delete(tuple[0]);
+ req._hookCtx = hookCtx;
+ req.onerror = errorHandler;
+ if (i === lastItem) req.onsuccess = hookedEventSuccessHandler(resolve);else req.onsuccess = successHandler;
+ }
+ }, function (err) {
+ hookCtx.onerror && hookCtx.onerror(err);
+ throw err;
+ });
+ }
+ });
+ }
-var _DateUtils = _interopRequireDefault(require("../utilities/DateUtils"));
+ props(Table.prototype, {
+ //
+ // Table Protected Methods
+ //
+ _trans: function getTransaction(mode, fn, writeLocked) {
+ var trans = this._tx || PSD.trans;
+ return trans && trans.db === db ? trans === PSD.trans ? trans._promise(mode, fn, writeLocked) : newScope(function () {
+ return trans._promise(mode, fn, writeLocked);
+ }, {
+ trans: trans,
+ transless: PSD.transless || PSD
+ }) : tempTransaction(mode, [this.name], fn);
+ },
+ _idbstore: function getIDBObjectStore(mode, fn, writeLocked) {
+ var tableName = this.name;
-var _caretPos = require("caret-pos");
+ function supplyIdbStore(resolve, reject, trans) {
+ if (trans.storeNames.indexOf(tableName) === -1) throw new exceptions.NotFound("Table" + tableName + " not part of transaction");
+ return fn(resolve, reject, trans.idbtrans.objectStore(tableName), trans);
+ }
-var _EventEmitter2 = _interopRequireDefault(require("../events/EventEmitter"));
+ return this._trans(mode, supplyIdbStore, writeLocked);
+ },
+ //
+ // Table Public Methods
+ //
+ get: function (keyOrCrit, cb) {
+ if (keyOrCrit && keyOrCrit.constructor === Object) return this.where(keyOrCrit).first(cb);
+ var self = this;
+ return this._idbstore(READONLY, function (resolve, reject, idbstore) {
+ var req = idbstore.get(keyOrCrit);
+ req.onerror = eventRejectHandler(reject);
+ req.onsuccess = wrap(function () {
+ resolve(self.hook.reading.fire(req.result));
+ }, reject);
+ }).then(cb);
+ },
+ where: function (indexOrCrit) {
+ if (typeof indexOrCrit === 'string') return new WhereClause(this, indexOrCrit);
+ if (isArray(indexOrCrit)) return new WhereClause(this, "[" + indexOrCrit.join('+') + "]"); // indexOrCrit is an object map of {[keyPath]:value}
+
+ var keyPaths = keys(indexOrCrit);
+ if (keyPaths.length === 1) // Only one critera. This was the easy case:
+ return this.where(keyPaths[0]).equals(indexOrCrit[keyPaths[0]]); // Multiple criterias.
+ // Let's try finding a compound index that matches all keyPaths in
+ // arbritary order:
+
+ var compoundIndex = this.schema.indexes.concat(this.schema.primKey).filter(function (ix) {
+ return ix.compound && keyPaths.every(function (keyPath) {
+ return ix.keyPath.indexOf(keyPath) >= 0;
+ }) && ix.keyPath.every(function (keyPath) {
+ return keyPaths.indexOf(keyPath) >= 0;
+ });
+ })[0];
+ if (compoundIndex && maxKey !== maxString) // Cool! We found such compound index
+ // and this browser supports compound indexes (maxKey !== maxString)!
+ return this.where(compoundIndex.name).equals(compoundIndex.keyPath.map(function (kp) {
+ return indexOrCrit[kp];
+ }));
+ if (!compoundIndex) console.warn("The query " + JSON.stringify(indexOrCrit) + " on " + this.name + " would benefit of a " + ("compound index [" + keyPaths.join('+') + "]")); // Ok, now let's fallback to finding at least one matching index
+ // and filter the rest.
+
+ var idxByName = this.schema.idxByName;
+ var simpleIndex = keyPaths.reduce(function (r, keyPath) {
+ return [r[0] || idxByName[keyPath], r[0] || !idxByName[keyPath] ? combine(r[1], function (x) {
+ return '' + getByKeyPath(x, keyPath) == '' + indexOrCrit[keyPath];
+ }) : r[1]];
+ }, [null, null]);
+ var idx = simpleIndex[0];
+ return idx ? this.where(idx.name).equals(indexOrCrit[idx.keyPath]).filter(simpleIndex[1]) : compoundIndex ? this.filter(simpleIndex[1]) : // Has compound but browser bad. Allow filter.
+ this.where(keyPaths).equals(''); // No index at all. Fail lazily.
+ },
+ count: function (cb) {
+ return this.toCollection().count(cb);
+ },
+ offset: function (offset) {
+ return this.toCollection().offset(offset);
+ },
+ limit: function (numRows) {
+ return this.toCollection().limit(numRows);
+ },
+ reverse: function () {
+ return this.toCollection().reverse();
+ },
+ filter: function (filterFunction) {
+ return this.toCollection().and(filterFunction);
+ },
+ each: function (fn) {
+ return this.toCollection().each(fn);
+ },
+ toArray: function (cb) {
+ return this.toCollection().toArray(cb);
+ },
+ orderBy: function (index) {
+ return new Collection(new WhereClause(this, isArray(index) ? "[" + index.join('+') + "]" : index));
+ },
+ toCollection: function () {
+ return new Collection(new WhereClause(this));
+ },
+ mapToClass: function (constructor, structure) {
+ ///
+ /// Map table to a javascript constructor function. Objects returned from the database will be instances of this class, making
+ /// it possible to the instanceOf operator as well as extending the class using constructor.prototype.method = function(){...}.
+ ///
+ /// Constructor function representing the class.
+ /// Helps IDE code completion by knowing the members that objects contain and not just the indexes. Also
+ /// know what type each member has. Example: {name: String, emailAddresses: [String], password}
+ this.schema.mappedClass = constructor;
+ var instanceTemplate = Object.create(constructor.prototype);
-var EditorEvent = _interopRequireWildcard(require("../events/EditorEvent"));
+ if (structure) {
+ // structure and instanceTemplate is for IDE code competion only while constructor.prototype is for actual inheritance.
+ applyStructure(instanceTemplate, structure);
+ }
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ this.schema.instanceTemplate = instanceTemplate; // Now, subscribe to the when("reading") event to make all objects that come out from this table inherit from given class
+ // no matter which method to use for reading (Table.get() or Table.where(...)... )
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
+ var readHook = function (obj) {
+ if (!obj) return obj; // No valid object. (Value is null). Return as is.
+ // Create a new object that derives from constructor:
-function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+ var res = Object.create(constructor.prototype); // Clone members:
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ for (var m in obj) {
+ if (hasOwn(obj, m)) try {
+ res[m] = obj[m];
+ } catch (_) {}
+ }
-function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
+ return res;
+ };
-function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
+ if (this.schema.readHook) {
+ this.hook.reading.unsubscribe(this.schema.readHook);
+ }
-function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
+ this.schema.readHook = readHook;
+ this.hook("reading", readHook);
+ return constructor;
+ },
+ defineClass: function (structure) {
+ ///
+ /// Define all members of the class that represents the table. This will help code completion of when objects are read from the database
+ /// as well as making it possible to extend the prototype of the returned constructor function.
+ ///
+ /// Helps IDE code completion by knowing the members that objects contain and not just the indexes. Also
+ /// know what type each member has. Example: {name: String, emailAddresses: [String], properties: {shoeSize: Number}}
+ return this.mapToClass(Dexie.defineClass(structure), structure);
+ },
+ bulkDelete: function (keys$$1) {
+ if (this.hook.deleting.fire === nop) {
+ return this._idbstore(READWRITE, function (resolve, reject, idbstore, trans) {
+ resolve(bulkDelete(idbstore, trans, keys$$1, false, nop));
+ });
+ } else {
+ return this.where(':id').anyOf(keys$$1).delete().then(function () {}); // Resolve with undefined.
+ }
+ },
+ bulkPut: function (objects, keys$$1) {
+ var _this = this;
-function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
+ return this._idbstore(READWRITE, function (resolve, reject, idbstore) {
+ if (!idbstore.keyPath && !_this.schema.primKey.auto && !keys$$1) throw new exceptions.InvalidArgument("bulkPut() with non-inbound keys requires keys array in second argument");
+ if (idbstore.keyPath && keys$$1) throw new exceptions.InvalidArgument("bulkPut(): keys argument invalid on tables with inbound keys");
+ if (keys$$1 && keys$$1.length !== objects.length) throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length");
+ if (objects.length === 0) return resolve(); // Caller provided empty list.
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
+ var done = function (result) {
+ if (errorList.length === 0) resolve(result);else reject(new BulkError(_this.name + ".bulkPut(): " + errorList.length + " of " + numObjs + " operations failed", errorList));
+ };
-function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
+ var req,
+ errorList = [],
+ errorHandler,
+ numObjs = objects.length,
+ table = _this;
-function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
+ if (_this.hook.creating.fire === nop && _this.hook.updating.fire === nop) {
+ //
+ // Standard Bulk (no 'creating' or 'updating' hooks to care about)
+ //
+ errorHandler = BulkErrorHandlerCatchAll(errorList);
-var TextEditor =
-/*#__PURE__*/
-function (_EventEmitter) {
- _inherits(TextEditor, _EventEmitter);
+ for (var i = 0, l = objects.length; i < l; ++i) {
+ req = keys$$1 ? idbstore.put(objects[i], keys$$1[i]) : idbstore.put(objects[i]);
+ req.onerror = errorHandler;
+ } // Only need to catch success or error on the last operation
+ // according to the IDB spec.
- //--------------------------
- // constructor
- //--------------------------
- function TextEditor(textEditor, scrollLimit) {
- var _this;
- _classCallCheck(this, TextEditor);
+ req.onerror = BulkErrorHandlerCatchAll(errorList, done);
+ req.onsuccess = eventSuccessHandler(done);
+ } else {
+ var effectiveKeys = keys$$1 || idbstore.keyPath && objects.map(function (o) {
+ return getByKeyPath(o, idbstore.keyPath);
+ }); // Generate map of {[key]: object}
- _this = _possibleConstructorReturn(this, _getPrototypeOf(TextEditor).call(this));
- hljs.initHighlightingOnLoad();
- _this.dateUtils = new _DateUtils.default();
- _this.textEditor = textEditor;
- _this.fixLimit = scrollLimit;
- _this.caretPos = null;
- _this.url = '';
+ var objectLookup = effectiveKeys && arrayToObject(effectiveKeys, function (key, i) {
+ return key != null && [key, objects[i]];
+ });
+ var promise = !effectiveKeys ? // Auto-incremented key-less objects only without any keys argument.
+ table.bulkAdd(objects) : // Keys provided. Either as inbound in provided objects, or as a keys argument.
+ // Begin with updating those that exists in DB:
+ table.where(':id').anyOf(effectiveKeys.filter(function (key) {
+ return key != null;
+ })).modify(function () {
+ this.value = objectLookup[this.primKey];
+ objectLookup[this.primKey] = null; // Mark as "don't add this"
+ }).catch(ModifyError, function (e) {
+ errorList = e.failures; // No need to concat here. These are the first errors added.
+ }).then(function () {
+ // Now, let's examine which items didnt exist so we can add them:
+ var objsToAdd = [],
+ keysToAdd = keys$$1 && []; // Iterate backwards. Why? Because if same key was used twice, just add the last one.
+
+ for (var i = effectiveKeys.length - 1; i >= 0; --i) {
+ var key = effectiveKeys[i];
+
+ if (key == null || objectLookup[key]) {
+ objsToAdd.push(objects[i]);
+ keys$$1 && keysToAdd.push(key);
+ if (key != null) objectLookup[key] = null; // Mark as "dont add again"
+ }
+ } // The items are in reverse order so reverse them before adding.
+ // Could be important in order to get auto-incremented keys the way the caller
+ // would expect. Could have used unshift instead of push()/reverse(),
+ // but: http://jsperf.com/unshift-vs-reverse
- var self = _assertThisInitialized(_assertThisInitialized(_this));
- _this.setInputs();
+ objsToAdd.reverse();
+ keys$$1 && keysToAdd.reverse();
+ return table.bulkAdd(objsToAdd, keysToAdd);
+ }).then(function (lastAddedKey) {
+ // Resolve with key of the last object in given arguments to bulkPut():
+ var lastEffectiveKey = effectiveKeys[effectiveKeys.length - 1]; // Key was provided.
- window.addEventListener("scroll", function (f) {
- var fixLimit = _this.fixLimit;
+ return lastEffectiveKey != null ? lastEffectiveKey : lastAddedKey;
+ });
+ promise.then(done).catch(BulkError, function (e) {
+ // Concat failure from ModifyError and reject using our 'done' method.
+ errorList = errorList.concat(e.failures);
+ done();
+ }).catch(reject);
+ }
+ }, "locked"); // If called from transaction scope, lock transaction til all steps are done.
+ },
+ bulkAdd: function (objects, keys$$1) {
+ var self = this,
+ creatingHook = this.hook.creating.fire;
+ return this._idbstore(READWRITE, function (resolve, reject, idbstore, trans) {
+ if (!idbstore.keyPath && !self.schema.primKey.auto && !keys$$1) throw new exceptions.InvalidArgument("bulkAdd() with non-inbound keys requires keys array in second argument");
+ if (idbstore.keyPath && keys$$1) throw new exceptions.InvalidArgument("bulkAdd(): keys argument invalid on tables with inbound keys");
+ if (keys$$1 && keys$$1.length !== objects.length) throw new exceptions.InvalidArgument("Arguments objects and keys must have the same length");
+ if (objects.length === 0) return resolve(); // Caller provided empty list.
+
+ function done(result) {
+ if (errorList.length === 0) resolve(result);else reject(new BulkError(self.name + ".bulkAdd(): " + errorList.length + " of " + numObjs + " operations failed", errorList));
+ }
+
+ var req,
+ errorList = [],
+ errorHandler,
+ successHandler,
+ numObjs = objects.length;
+
+ if (creatingHook !== nop) {
+ //
+ // There are subscribers to hook('creating')
+ // Must behave as documented.
+ //
+ var keyPath = idbstore.keyPath,
+ hookCtx;
+ errorHandler = BulkErrorHandlerCatchAll(errorList, null, true);
+ successHandler = hookedEventSuccessHandler(null);
+ tryCatch(function () {
+ for (var i = 0, l = objects.length; i < l; ++i) {
+ hookCtx = {
+ onerror: null,
+ onsuccess: null
+ };
+ var key = keys$$1 && keys$$1[i];
+ var obj = objects[i],
+ effectiveKey = keys$$1 ? key : keyPath ? getByKeyPath(obj, keyPath) : undefined,
+ keyToUse = creatingHook.call(hookCtx, effectiveKey, obj, trans);
+
+ if (effectiveKey == null && keyToUse != null) {
+ if (keyPath) {
+ obj = deepClone(obj);
+ setByKeyPath(obj, keyPath, keyToUse);
+ } else {
+ key = keyToUse;
+ }
+ }
- if (window.pageYOffset >= fixLimit) {
- document.getElementById('edit-control').style.position = "fixed";
- } else {
- document.getElementById('edit-control').style.position = "relative";
- }
- });
+ req = key != null ? idbstore.add(obj, key) : idbstore.add(obj);
+ req._hookCtx = hookCtx;
- _this.refresh();
+ if (i < l - 1) {
+ req.onerror = errorHandler;
+ if (hookCtx.onsuccess) req.onsuccess = successHandler;
+ }
+ }
+ }, function (err) {
+ hookCtx.onerror && hookCtx.onerror(err);
+ throw err;
+ });
+ req.onerror = BulkErrorHandlerCatchAll(errorList, done, true);
+ req.onsuccess = hookedEventSuccessHandler(done);
+ } else {
+ //
+ // Standard Bulk (no 'creating' hook to care about)
+ //
+ errorHandler = BulkErrorHandlerCatchAll(errorList);
- return _this;
- } //--------------------------
- // methods
- //--------------------------
+ for (var i = 0, l = objects.length; i < l; ++i) {
+ req = keys$$1 ? idbstore.add(objects[i], keys$$1[i]) : idbstore.add(objects[i]);
+ req.onerror = errorHandler;
+ } // Only need to catch success or error on the last operation
+ // according to the IDB spec.
- _createClass(TextEditor, [{
- key: "setInputs",
- value: function setInputs() {
- var _this2 = this;
+ req.onerror = BulkErrorHandlerCatchAll(errorList, done);
+ req.onsuccess = eventSuccessHandler(done);
+ }
+ });
+ },
+ add: function (obj, key) {
+ ///
+ /// Add an object to the database. In case an object with same primary key already exists, the object will not be added.
+ ///
+ /// A javascript object to insert
+ /// Primary key
+ var creatingHook = this.hook.creating.fire;
+ return this._idbstore(READWRITE, function (resolve, reject, idbstore, trans) {
+ var hookCtx = {
+ onsuccess: null,
+ onerror: null
+ };
- var self = this;
- var editorButtons = document.querySelectorAll('.editor-button');
+ if (creatingHook !== nop) {
+ var effectiveKey = key != null ? key : idbstore.keyPath ? getByKeyPath(obj, idbstore.keyPath) : undefined;
+ var keyToUse = creatingHook.call(hookCtx, effectiveKey, obj, trans); // Allow subscribers to when("creating") to generate the key.
- for (var i = 0, length = editorButtons.length; i < length; i++) {
- editorButtons[i].addEventListener('click', function (e) {
- return _this2.handleEditorOption(e);
- }, false);
- }
+ if (effectiveKey == null && keyToUse != null) {
+ if (idbstore.keyPath) setByKeyPath(obj, idbstore.keyPath, keyToUse);else key = keyToUse;
+ }
+ }
- this.textEditor.addEventListener('input', function (f) {
- if (f.inputType == "insertParagraph") {
- var caret = (0, _caretPos.position)(self.textEditor).pos + 1;
- var spiffed = hljs.highlight('markdown', self.textEditor.innerText).value;
- var temp = document.createElement("div");
- temp.innerText = spiffed;
- self.textEditor.innerHTML = temp.innerText;
- (0, _caretPos.position)(self.textEditor, caret);
- } else {
- self.refresh();
+ try {
+ var req = key != null ? idbstore.add(obj, key) : idbstore.add(obj);
+ req._hookCtx = hookCtx;
+ req.onerror = hookedEventRejectHandler(reject);
+ req.onsuccess = hookedEventSuccessHandler(function (result) {
+ // TODO: Remove these two lines in next major release (2.0?)
+ // It's no good practice to have side effects on provided parameters
+ var keyPath = idbstore.keyPath;
+ if (keyPath) setByKeyPath(obj, keyPath, result);
+ resolve(result);
+ });
+ } catch (e) {
+ if (hookCtx.onerror) hookCtx.onerror(e);
+ throw e;
}
});
- }
- }, {
- key: "refresh",
- value: function refresh() {
- var caret = (0, _caretPos.position)(this.textEditor).pos;
- var spiffed = hljs.highlight('markdown', this.textEditor.innerText).value;
- var temp = document.createElement("div");
- temp.innerText = spiffed;
- this.textEditor.innerHTML = temp.innerText;
- (0, _caretPos.position)(this.textEditor, caret);
- }
- }, {
- key: "notify",
- value: function notify(type, data) {
- switch (type) {
- case DataEvent.POST_UPDATED:
- document.getElementById('submit-update').classList.add('icon-hide');
- document.getElementById('submit-good').classList.remove('icon-hide');
- document.getElementById('edit-update').classList.remove('submit-start');
- document.getElementById('edit-update').classList.add('submit-cool');
- setTimeout(function (f) {
- document.getElementById('submit-update').classList.remove('icon-hide');
- document.getElementById('submit-good').classList.add('icon-hide');
- document.getElementById('edit-update').classList.add('submit-start');
- document.getElementById('edit-update').classList.remove('submit-cool');
- }, 2000);
- break;
+ },
+ put: function (obj, key) {
+ var _this = this; ///
+ /// Add an object to the database but in case an object with same primary key alread exists, the existing one will get updated.
+ ///
+ /// A javascript object to insert or update
+ /// Primary key
+
+
+ var creatingHook = this.hook.creating.fire,
+ updatingHook = this.hook.updating.fire;
+
+ if (creatingHook !== nop || updatingHook !== nop) {
+ //
+ // People listens to when("creating") or when("updating") events!
+ // We must know whether the put operation results in an CREATE or UPDATE.
+ //
+ var keyPath = this.schema.primKey.keyPath;
+ var effectiveKey = key !== undefined ? key : keyPath && getByKeyPath(obj, keyPath);
+ if (effectiveKey == null) return this.add(obj); // Since key is optional, make sure we get it from obj if not provided
+ // Primary key exist. Lock transaction and try modifying existing. If nothing modified, call add().
+ // clone obj before this async call. If caller modifies obj the line after put(), the IDB spec requires that it should not affect operation.
+
+ obj = deepClone(obj);
+ return this._trans(READWRITE, function () {
+ return _this.where(":id").equals(effectiveKey).modify(function () {
+ // Replace extisting value with our object
+ // CRUD event firing handled in Collection.modify()
+ this.value = obj;
+ }).then(function (count) {
+ return count === 0 ? _this.add(obj, key) : effectiveKey;
+ });
+ }, "locked"); // Lock needed because operation is splitted into modify() and add().
+ } else {
+ // Use the standard IDB put() method.
+ return this._idbstore(READWRITE, function (resolve, reject, idbstore) {
+ var req = key !== undefined ? idbstore.put(obj, key) : idbstore.put(obj);
+ req.onerror = eventRejectHandler(reject);
+ req.onsuccess = wrap(function (ev) {
+ var keyPath = idbstore.keyPath;
+ if (keyPath) setByKeyPath(obj, keyPath, ev.target.result);
+ resolve(req.result);
+ });
+ });
+ }
+ },
+ 'delete': function (key) {
+ /// Primary key of the object to delete
+ if (this.hook.deleting.subscribers.length) {
+ // People listens to when("deleting") event. Must implement delete using Collection.delete() that will
+ // call the CRUD event. Only Collection.delete() will know whether an object was actually deleted.
+ return this.where(":id").equals(key).delete();
+ } else {
+ // No one listens. Use standard IDB delete() method.
+ return this._idbstore(READWRITE, function (resolve, reject, idbstore) {
+ var req = idbstore.delete(key);
+ req.onerror = eventRejectHandler(reject);
+ req.onsuccess = wrap(function () {
+ resolve(req.result);
+ });
+ });
+ }
+ },
+ clear: function () {
+ if (this.hook.deleting.subscribers.length) {
+ // People listens to when("deleting") event. Must implement delete using Collection.delete() that will
+ // call the CRUD event. Only Collection.delete() will knows which objects that are actually deleted.
+ return this.toCollection().delete();
+ } else {
+ return this._idbstore(READWRITE, function (resolve, reject, idbstore) {
+ var req = idbstore.clear();
+ req.onerror = eventRejectHandler(reject);
+ req.onsuccess = wrap(function () {
+ resolve(req.result);
+ });
+ });
+ }
+ },
+ update: function (keyOrObject, modifications) {
+ if (_typeof(modifications) !== 'object' || isArray(modifications)) throw new exceptions.InvalidArgument("Modifications must be an object.");
- case DataEvent.POST_ADDED:
- // do nothing
- break;
+ if (_typeof(keyOrObject) === 'object' && !isArray(keyOrObject)) {
+ // object to modify. Also modify given object with the modifications:
+ keys(modifications).forEach(function (keyPath) {
+ setByKeyPath(keyOrObject, keyPath, modifications[keyPath]);
+ });
+ var key = getByKeyPath(keyOrObject, this.schema.primKey.keyPath);
+ if (key === undefined) return rejection(new exceptions.InvalidArgument("Given object does not contain its primary key"));
+ return this.where(":id").equals(key).modify(modifications);
+ } else {
+ // key to modify
+ return this.where(":id").equals(keyOrObject).modify(modifications);
+ }
+ }
+ }); //
+ //
+ //
+ // Transaction Class
+ //
+ //
+ //
- case EditorEvent.EDITOR_UPLOAD_POST_IMAGE:
- (0, _caretPos.position)(this.textEditor, this.caretPos);
- var sel, range, pulled;
- sel = window.getSelection(); //console.log(sel)
- //console.log(note.message)
+ function Transaction(mode, storeNames, dbschema, parent) {
+ var _this = this; ///
+ /// Transaction class. Represents a database transaction. All operations on db goes through a Transaction.
+ ///
+ /// Any of "readwrite" or "readonly"
+ /// Array of table names to operate on
+
+
+ this.db = db;
+ this.mode = mode;
+ this.storeNames = storeNames;
+ this.idbtrans = null;
+ this.on = Events(this, "complete", "error", "abort");
+ this.parent = parent || null;
+ this.active = true;
+ this._reculock = 0;
+ this._blockedFuncs = [];
+ this._resolve = null;
+ this._reject = null;
+ this._waitingFor = null;
+ this._waitingQueue = null;
+ this._spinCount = 0; // Just for debugging waitFor()
+
+ this._completion = new Promise(function (resolve, reject) {
+ _this._resolve = resolve;
+ _this._reject = reject;
+ });
- if (sel.rangeCount) {
- range = sel.getRangeAt(0);
- pulled = sel.getRangeAt(0).toString();
- range.deleteContents();
- range.insertNode(document.createTextNode("![image alt text](" + data + " 'image title')"));
- }
+ this._completion.then(function () {
+ _this.active = false;
- this.refresh();
- break;
- }
- } //--------------------------
- // event handlers
- //--------------------------
+ _this.on.complete.fire();
+ }, function (e) {
+ var wasActive = _this.active;
+ _this.active = false;
- }, {
- key: "handleEditorOption",
- value: function handleEditorOption(e) {
- e.preventDefault();
- var self = this;
- var sel, range, pulled;
- sel = window.getSelection(); //console.log(sel)
+ _this.on.error.fire(e);
- if (sel.rangeCount) {
- range = sel.getRangeAt(0);
- pulled = sel.getRangeAt(0).toString();
- range.deleteContents();
+ _this.parent ? _this.parent._reject(e) : wasActive && _this.idbtrans && _this.idbtrans.abort();
+ return rejection(e); // Indicate we actually DO NOT catch this error.
+ });
+ }
- switch (e.target.id) {
- case "edit-bold":
- range.insertNode(document.createTextNode("**" + pulled + "**"));
- break;
+ props(Transaction.prototype, {
+ //
+ // Transaction Protected Methods (not required by API users, but needed internally and eventually by dexie extensions)
+ //
+ _lock: function () {
+ assert(!PSD.global); // Locking and unlocking reuires to be within a PSD scope.
+ // Temporary set all requests into a pending queue if they are called before database is ready.
- case "edit-italic":
- range.insertNode(document.createTextNode("*" + pulled + "*"));
- break;
+ ++this._reculock; // Recursive read/write lock pattern using PSD (Promise Specific Data) instead of TLS (Thread Local Storage)
- case "edit-strikethrough":
- range.insertNode(document.createTextNode("" + pulled + ""));
- break;
+ if (this._reculock === 1 && !PSD.global) PSD.lockOwnerFor = this;
+ return this;
+ },
+ _unlock: function () {
+ assert(!PSD.global); // Locking and unlocking reuires to be within a PSD scope.
- case "edit-header1":
- range.insertNode(document.createTextNode("# " + pulled));
- break;
+ if (--this._reculock === 0) {
+ if (!PSD.global) PSD.lockOwnerFor = null;
- case "edit-header2":
- range.insertNode(document.createTextNode("## " + pulled));
- break;
+ while (this._blockedFuncs.length > 0 && !this._locked()) {
+ var fnAndPSD = this._blockedFuncs.shift();
- case "edit-header3":
- range.insertNode(document.createTextNode("### " + pulled));
- break;
+ try {
+ usePSD(fnAndPSD[1], fnAndPSD[0]);
+ } catch (e) {}
+ }
+ }
- case "edit-image":
- this.caretPos = (0, _caretPos.position)(this.textEditor).pos;
- this.emitEvent(EditorEvent.EDITOR_UPLOAD_POST_IMAGE);
- break;
-
- case "submit-save":
- case "edit-save":
- this.emitEvent(EditorEvent.EDITOR_SAVE);
- break;
+ return this;
+ },
+ _locked: function () {
+ // Checks if any write-lock is applied on this transaction.
+ // To simplify the Dexie API for extension implementations, we support recursive locks.
+ // This is accomplished by using "Promise Specific Data" (PSD).
+ // PSD data is bound to a Promise and any child Promise emitted through then() or resolve( new Promise() ).
+ // PSD is local to code executing on top of the call stacks of any of any code executed by Promise():
+ // * callback given to the Promise() constructor (function (resolve, reject){...})
+ // * callbacks given to then()/catch()/finally() methods (function (value){...})
+ // If creating a new independant Promise instance from within a Promise call stack, the new Promise will derive the PSD from the call stack of the parent Promise.
+ // Derivation is done so that the inner PSD __proto__ points to the outer PSD.
+ // PSD.lockOwnerFor will point to current transaction object if the currently executing PSD scope owns the lock.
+ return this._reculock && PSD.lockOwnerFor !== this;
+ },
+ create: function (idbtrans) {
+ var _this = this;
- case "submit-update":
- case "edit-update":
- this.emitEvent(EditorEvent.EDITOR_UPDATE);
- break;
+ if (!this.mode) return this;
+ assert(!this.idbtrans);
- case "edit-link":
- range.insertNode(document.createTextNode("[" + pulled + "](PASTE URL HERE)"));
- break;
+ if (!idbtrans && !idbdb) {
+ switch (dbOpenError && dbOpenError.name) {
+ case "DatabaseClosedError":
+ // Errors where it is no difference whether it was caused by the user operation or an earlier call to db.open()
+ throw new exceptions.DatabaseClosed(dbOpenError);
- case "edit-delete":
- this.emitEvent(EditorEvent.EDITOR_DELETE);
- break;
+ case "MissingAPIError":
+ // Errors where it is no difference whether it was caused by the user operation or an earlier call to db.open()
+ throw new exceptions.MissingAPI(dbOpenError.message, dbOpenError);
default:
- //range.insertNode(document.createTextNode("[" + self.url + "](PASTE URL HERE)"));
- break;
+ // Make it clear that the user operation was not what caused the error - the error had occurred earlier on db.open()!
+ throw new exceptions.OpenFailed(dbOpenError);
}
}
- this.refresh();
- }
- }]);
-
- return TextEditor;
-}(_EventEmitter2.default);
+ if (!this.active) throw new exceptions.TransactionInactive();
+ assert(this._completion._state === null);
+ idbtrans = this.idbtrans = idbtrans || idbdb.transaction(safariMultiStoreFix(this.storeNames), this.mode);
+ idbtrans.onerror = wrap(function (ev) {
+ preventDefault(ev); // Prohibit default bubbling to window.error
-var _default = TextEditor;
-exports.default = _default;
-},{"../events/DataEvent":"../../../../brain/tools/events/DataEvent.js","../utilities/DateUtils":"../../../../brain/tools/utilities/DateUtils.js","caret-pos":"../../../../node_modules/caret-pos/lib/esm2015/main.js","../events/EventEmitter":"../../../../brain/tools/events/EventEmitter.js","../events/EditorEvent":"../../../../brain/tools/events/EditorEvent.js"}],"controllers/PostEditor.js":[function(require,module,exports) {
-"use strict";
+ _this._reject(idbtrans.error);
+ });
+ idbtrans.onabort = wrap(function (ev) {
+ preventDefault(ev);
+ _this.active && _this._reject(new exceptions.Abort(idbtrans.error));
+ _this.active = false;
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
+ _this.on("abort").fire(ev);
+ });
+ idbtrans.oncomplete = wrap(function () {
+ _this.active = false;
-var _DataUtils = _interopRequireWildcard(require("../../../../../brain/tools/utilities/DataUtils"));
+ _this._resolve();
+ });
+ return this;
+ },
+ _promise: function (mode, fn, bWriteLock) {
+ var _this = this;
-var DataEvent = _interopRequireWildcard(require("../../../../../brain/tools/events/DataEvent"));
+ if (mode === READWRITE && this.mode !== READWRITE) return rejection(new exceptions.ReadOnly("Transaction is readonly"));
+ if (!this.active) return rejection(new exceptions.TransactionInactive());
-var _Animate = _interopRequireDefault(require("../../../../../brain/tools/effects/Animate"));
+ if (this._locked()) {
+ return new Promise(function (resolve, reject) {
+ _this._blockedFuncs.push([function () {
+ _this._promise(mode, fn, bWriteLock).then(resolve, reject);
+ }, PSD]);
+ });
+ } else if (bWriteLock) {
+ return newScope(function () {
+ var p = new Promise(function (resolve, reject) {
+ _this._lock();
-var _PostActions = _interopRequireDefault(require("../actions/PostActions"));
+ var rv = fn(resolve, reject, _this);
+ if (rv && rv.then) rv.then(resolve, reject);
+ });
+ p.finally(function () {
+ return _this._unlock();
+ });
+ p._lib = true;
+ return p;
+ });
+ } else {
+ var p = new Promise(function (resolve, reject) {
+ var rv = fn(resolve, reject, _this);
+ if (rv && rv.then) rv.then(resolve, reject);
+ });
+ p._lib = true;
+ return p;
+ }
+ },
+ _root: function () {
+ return this.parent ? this.parent._root() : this;
+ },
+ waitFor: function (promise) {
+ // Always operate on the root transaction (in case this is a sub stransaction)
+ var root = this._root(); // For stability reasons, convert parameter to promise no matter what type is passed to waitFor().
+ // (We must be able to call .then() on it.)
-var EditorEvent = _interopRequireWildcard(require("../../../../../brain/tools/events/EditorEvent"));
-var _tinyDatePicker = _interopRequireDefault(require("tiny-date-picker"));
+ promise = Promise.resolve(promise);
-var _DateUtils = _interopRequireDefault(require("../../../../../brain/tools/utilities/DateUtils"));
+ if (root._waitingFor) {
+ // Already called waitFor(). Wait for both to complete.
+ root._waitingFor = root._waitingFor.then(function () {
+ return promise;
+ });
+ } else {
+ // We're not in waiting state. Start waiting state.
+ root._waitingFor = promise;
+ root._waitingQueue = []; // Start interacting with indexedDB until promise completes:
-var _TextEditor = _interopRequireDefault(require("../../../../../brain/tools/ui/TextEditor"));
+ var store = root.idbtrans.objectStore(root.storeNames[0]);
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ (function spin() {
+ ++root._spinCount; // For debugging only
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
+ while (root._waitingQueue.length) {
+ root._waitingQueue.shift()();
+ }
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ if (root._waitingFor) store.get(-Infinity).onsuccess = spin;
+ })();
+ }
-function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
+ var currentWaitPromise = root._waitingFor;
+ return new Promise(function (resolve, reject) {
+ promise.then(function (res) {
+ return root._waitingQueue.push(wrap(resolve.bind(null, res)));
+ }, function (err) {
+ return root._waitingQueue.push(wrap(reject.bind(null, err)));
+ }).finally(function () {
+ if (root._waitingFor === currentWaitPromise) {
+ // No one added a wait after us. Safe to stop the spinning.
+ root._waitingFor = null;
+ }
+ });
+ });
+ },
+ //
+ // Transaction Public Properties and Methods
+ //
+ abort: function () {
+ this.active && this._reject(new exceptions.Abort());
+ this.active = false;
+ },
+ tables: {
+ get: deprecated("Transaction.tables", function () {
+ return allTables;
+ })
+ },
+ table: function (name) {
+ var table = db.table(name); // Don't check that table is part of transaction. It must fail lazily!
-function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
+ return new Table(name, table.schema, this);
+ }
+ }); //
+ //
+ //
+ // WhereClause
+ //
+ //
+ //
-var PostEditor =
-/*#__PURE__*/
-function () {
- //--------------------------
- // constructor
- //--------------------------
- function PostEditor() {
- var _this = this;
+ function WhereClause(table, index, orCollection) {
+ ///
+ ///
+ ///
+ this._ctx = {
+ table: table,
+ index: index === ":id" ? null : index,
+ or: orCollection
+ };
+ }
- _classCallCheck(this, PostEditor);
+ props(WhereClause.prototype, function () {
+ // WhereClause private methods
+ function fail(collectionOrWhereClause, err, T) {
+ var collection = collectionOrWhereClause instanceof WhereClause ? new Collection(collectionOrWhereClause) : collectionOrWhereClause;
+ collection._ctx.error = T ? new T(err) : new TypeError(err);
+ return collection;
+ }
- //reframe('iframe');
- var self = this;
- this.uploadFiles;
- this.anim = new _Animate.default();
- this.dataUtils = new _DataUtils.default();
- this.dateUtils = new _DateUtils.default();
+ function emptyCollection(whereClause) {
+ return new Collection(whereClause, function () {
+ return IDBKeyRange.only("");
+ }).limit(0);
+ }
- if (document.getElementById('edit-post-text')) {
- this.editor = new _TextEditor.default(document.getElementById('edit-post-text'), document.getElementById('header').offsetHeight + document.getElementById('post-header').offsetHeight + document.getElementById('post-feature').offsetHeight);
- this.editor.addListener(EditorEvent.EDITOR_DELETE, function (f) {
- return _this.handleEditorOptions(EditorEvent.EDITOR_DELETE);
- }, false);
- this.editor.addListener(EditorEvent.EDITOR_UPLOAD_POST_IMAGE, function (f) {
- return _this.handleEditorOptions(EditorEvent.EDITOR_UPLOAD_POST_IMAGE);
- }, false);
- this.editor.addListener(EditorEvent.EDITOR_UPDATE, function (f) {
- return _this.handleEditorOptions(EditorEvent.EDITOR_UPDATE);
- }, false);
- this.editor.addListener(EditorEvent.EDITOR_SAVE, function (f) {
- return _this.handleEditorOptions(EditorEvent.EDITOR_SAVE);
- }, false);
- document.getElementById('post-image').addEventListener('change', function (e) {
- return _this.handlePostImageAdd(e);
- }, false);
- (0, _tinyDatePicker.default)(document.getElementById('post-date'), {
- mode: 'dp-below',
- format: function format(date) {
- //return date;
- return self.dateUtils.getDate('origin', date);
- }
- });
+ function upperFactory(dir) {
+ return dir === "next" ? function (s) {
+ return s.toUpperCase();
+ } : function (s) {
+ return s.toLowerCase();
+ };
}
- this.start();
- } //--------------------------
- // methods
- //--------------------------
+ function lowerFactory(dir) {
+ return dir === "next" ? function (s) {
+ return s.toLowerCase();
+ } : function (s) {
+ return s.toUpperCase();
+ };
+ }
+ function nextCasing(key, lowerKey, upperNeedle, lowerNeedle, cmp, dir) {
+ var length = Math.min(key.length, lowerNeedle.length);
+ var llp = -1;
- _createClass(PostEditor, [{
- key: "start",
- value: function start() {
- var _this2 = this;
+ for (var i = 0; i < length; ++i) {
+ var lwrKeyChar = lowerKey[i];
- var self = this;
- new _Animate.default().object({
- targets: document.getElementById('loader'),
- duration: 300,
- opacity: 0,
- easing: 'easeInOutQuad',
- complete: function complete() {
- document.getElementById('loader').style.display = 'none';
- document.getElementById('loader').style.visibility = 'hidden';
- new _Animate.default().object({
- targets: document.getElementById('header'),
- duration: 10,
- opacity: 1,
- easing: 'easeInOutQuad',
- complete: function complete() {
- if (document.getElementById('the-intro')) document.getElementById('the-intro').style.opacity = 1;
- if (document.getElementById('blog-entry')) document.getElementById('blog-entry').style.opacity = 1;
- }
- });
+ if (lwrKeyChar !== lowerNeedle[i]) {
+ if (cmp(key[i], upperNeedle[i]) < 0) return key.substr(0, i) + upperNeedle[i] + upperNeedle.substr(i + 1);
+ if (cmp(key[i], lowerNeedle[i]) < 0) return key.substr(0, i) + lowerNeedle[i] + upperNeedle.substr(i + 1);
+ if (llp >= 0) return key.substr(0, llp) + lowerKey[llp] + upperNeedle.substr(llp + 1);
+ return null;
}
- });
- if (document.getElementById('featured-image-drop')) {
- document.getElementById('featured-image-drop').addEventListener('dragover', this.handleDragOver, false);
- document.getElementById('featured-image-drop').addEventListener('drop', this.handleDrop, false);
- document.getElementById('featured-click').addEventListener('change', this.handleClicked, false);
+ if (cmp(key[i], lwrKeyChar) < 0) llp = i;
+ }
- if (document.getElementById('new-upload-link')) {
- document.getElementById('new-upload-link').addEventListener('click', function (e) {
- document.getElementById('featured-click').click();
- });
- }
+ if (length < lowerNeedle.length && dir === "next") return key + upperNeedle.substr(key.length);
+ if (length < key.length && dir === "prev") return key.substr(0, upperNeedle.length);
+ return llp < 0 ? null : key.substr(0, llp) + lowerNeedle[llp] + upperNeedle.substr(llp + 1);
+ }
- var optionButtons = document.querySelectorAll('.post-option-btn');
+ function addIgnoreCaseAlgorithm(whereClause, match, needles, suffix) {
+ ///
+ var upper,
+ lower,
+ compare,
+ upperNeedles,
+ lowerNeedles,
+ direction,
+ nextKeySuffix,
+ needlesLen = needles.length;
- for (var i = 0, length = optionButtons.length; i < length; i++) {
- optionButtons[i].addEventListener('click', function (e) {
- return _this2.handlePostOptions(e);
- }, false);
- }
+ if (!needles.every(function (s) {
+ return typeof s === 'string';
+ })) {
+ return fail(whereClause, STRING_EXPECTED);
}
- } //--------------------------
- // event handlers
- //--------------------------
-
- }, {
- key: "handlePostOptions",
- value: function handlePostOptions(e) {
- var currentOption;
- switch (e.target.id) {
- case "option-page-icon":
- case "option-page":
- currentOption = document.getElementById('option-page');
- break;
+ function initDirection(dir) {
+ upper = upperFactory(dir);
+ lower = lowerFactory(dir);
+ compare = dir === "next" ? simpleCompare : simpleCompareReverse;
+ var needleBounds = needles.map(function (needle) {
+ return {
+ lower: lower(needle),
+ upper: upper(needle)
+ };
+ }).sort(function (a, b) {
+ return compare(a.lower, b.lower);
+ });
+ upperNeedles = needleBounds.map(function (nb) {
+ return nb.upper;
+ });
+ lowerNeedles = needleBounds.map(function (nb) {
+ return nb.lower;
+ });
+ direction = dir;
+ nextKeySuffix = dir === "next" ? "" : suffix;
+ }
- case "option-feature-icon":
- case "option-feature":
- currentOption = document.getElementById('option-feature');
- break;
+ initDirection("next");
+ var c = new Collection(whereClause, function () {
+ return IDBKeyRange.bound(upperNeedles[0], lowerNeedles[needlesLen - 1] + suffix);
+ });
- case "option-published-icon":
- case "option-published":
- currentOption = document.getElementById('option-published');
- break;
- }
+ c._ondirectionchange = function (direction) {
+ // This event onlys occur before filter is called the first time.
+ initDirection(direction);
+ };
- var active = currentOption.getAttribute('data-active');
- active == 'false' ? currentOption.setAttribute('data-active', 'true') : currentOption.setAttribute('data-active', 'false');
- }
- }, {
- key: "handleEditorOptions",
- value: function handleEditorOptions(e) {
- var _this3 = this;
+ var firstPossibleNeedle = 0;
- switch (e) {
- case EditorEvent.EDITOR_SAVE:
- case EditorEvent.EDITOR_UPDATE:
- var edit = false;
- if (e == EditorEvent.EDITOR_UPDATE) edit = true;
- new _PostActions.default().submitPost(edit, PostEditor.uploadFiles).then(function (response) {
- var note = JSON.parse(response['response']['request'].response);
+ c._addAlgorithm(function (cursor, advance, resolve) {
+ ///
+ ///
+ ///
+ var key = cursor.key;
+ if (typeof key !== 'string') return false;
+ var lowerKey = lower(key);
- _this3.editor.notify(note.message, note.postID);
+ if (match(lowerKey, lowerNeedles, firstPossibleNeedle)) {
+ return true;
+ } else {
+ var lowestPossibleCasing = null;
- if (note.message == DataEvent.POST_ADDED) window.location = "/@/dashboard/posts/edit/" + note.postID;
- }).catch(function (err) {
- console.log(err);
- });
- break;
+ for (var i = firstPossibleNeedle; i < needlesLen; ++i) {
+ var casing = nextCasing(key, lowerKey, upperNeedles[i], lowerNeedles[i], compare, direction);
+ if (casing === null && lowestPossibleCasing === null) firstPossibleNeedle = i + 1;else if (lowestPossibleCasing === null || compare(lowestPossibleCasing, casing) > 0) {
+ lowestPossibleCasing = casing;
+ }
+ }
- case EditorEvent.EDITOR_DELETE:
- if (confirm('Aye! You know you\'re deleting this post, right?')) {
- new _PostActions.default().deletePost().then(function (response) {
- var note = JSON.parse(response['response']['request'].response);
- window.location = "/@/dashboard/posts/"; //console.log(note);
- }).catch(function (err) {
- console.log(err);
+ if (lowestPossibleCasing !== null) {
+ advance(function () {
+ cursor.continue(lowestPossibleCasing + nextKeySuffix);
});
- } else {// Do nothing!
+ } else {
+ advance(resolve);
}
- break;
-
- case EditorEvent.EDITOR_UPLOAD_POST_IMAGE:
- document.getElementById('post-image').click();
- break;
- }
- }
- }, {
- key: "handleDragOver",
- value: function handleDragOver(e) {
- e.stopPropagation();
- e.preventDefault();
- e.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
- }
- }, {
- key: "handleClicked",
- value: function handleClicked(e) {
- e.stopPropagation();
- e.preventDefault(); //console.log("IMAGES " + e.target.files);
+ return false;
+ }
+ });
- PostEditor.uploadFiles = e.target.files;
+ return c;
+ } //
+ // WhereClause public methods
+ //
- for (var i = 0, f; f = PostEditor.uploadFiles[i]; i++) {
- // Only process image files.
- if (!f.type.match('image.*')) {
- continue;
- }
- var reader = new FileReader(); // Closure to capture the file information.
+ return {
+ between: function (lower, upper, includeLower, includeUpper) {
+ ///
+ /// Filter out records whose where-field lays between given lower and upper values. Applies to Strings, Numbers and Dates.
+ ///
+ ///
+ ///
+ /// Whether items that equals lower should be included. Default true.
+ /// Whether items that equals upper should be included. Default false.
+ ///
+ includeLower = includeLower !== false; // Default to true
+
+ includeUpper = includeUpper === true; // Default to false
- reader.onload = function (theFile) {
- return function (f) {
- // Render thumbnail.
- var image = document.createElement('img');
- image.src = f.target.result;
- image.title = escape(theFile.name);
- var span = document.createElement('div');
- span.innerHTML = [''].join(''); //document.getElementById('featured-image-drop').insertBefore(span, null);
+ try {
+ if (cmp(lower, upper) > 0 || cmp(lower, upper) === 0 && (includeLower || includeUpper) && !(includeLower && includeUpper)) return emptyCollection(this); // Workaround for idiotic W3C Specification that DataError must be thrown if lower > upper. The natural result would be to return an empty collection.
- document.getElementById('featured-image-drop').innerHTML = '';
- document.getElementById('featured-image-drop').appendChild(image);
- };
- }(f); // Read in the image file as a data URL.
-
-
- reader.readAsDataURL(f);
- }
- }
- }, {
- key: "handleDrop",
- value: function handleDrop(e) {
- e.stopPropagation();
- e.preventDefault();
- PostEditor.uploadFiles = e.dataTransfer.files; //console.log(MemberArea.uploadFiles.length);
+ return new Collection(this, function () {
+ return IDBKeyRange.bound(lower, upper, !includeLower, !includeUpper);
+ });
+ } catch (e) {
+ return fail(this, INVALID_KEY_ARGUMENT);
+ }
+ },
+ equals: function (value) {
+ return new Collection(this, function () {
+ return IDBKeyRange.only(value);
+ });
+ },
+ above: function (value) {
+ return new Collection(this, function () {
+ return IDBKeyRange.lowerBound(value, true);
+ });
+ },
+ aboveOrEqual: function (value) {
+ return new Collection(this, function () {
+ return IDBKeyRange.lowerBound(value);
+ });
+ },
+ below: function (value) {
+ return new Collection(this, function () {
+ return IDBKeyRange.upperBound(value, true);
+ });
+ },
+ belowOrEqual: function (value) {
+ return new Collection(this, function () {
+ return IDBKeyRange.upperBound(value);
+ });
+ },
+ startsWith: function (str) {
+ ///
+ if (typeof str !== 'string') return fail(this, STRING_EXPECTED);
+ return this.between(str, str + maxString, true, true);
+ },
+ startsWithIgnoreCase: function (str) {
+ ///
+ if (str === "") return this.startsWith(str);
+ return addIgnoreCaseAlgorithm(this, function (x, a) {
+ return x.indexOf(a[0]) === 0;
+ }, [str], maxString);
+ },
+ equalsIgnoreCase: function (str) {
+ ///
+ return addIgnoreCaseAlgorithm(this, function (x, a) {
+ return x === a[0];
+ }, [str], "");
+ },
+ anyOfIgnoreCase: function () {
+ var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments);
+ if (set.length === 0) return emptyCollection(this);
+ return addIgnoreCaseAlgorithm(this, function (x, a) {
+ return a.indexOf(x) !== -1;
+ }, set, "");
+ },
+ startsWithAnyOfIgnoreCase: function () {
+ var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments);
+ if (set.length === 0) return emptyCollection(this);
+ return addIgnoreCaseAlgorithm(this, function (x, a) {
+ return a.some(function (n) {
+ return x.indexOf(n) === 0;
+ });
+ }, set, maxString);
+ },
+ anyOf: function () {
+ var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments);
+ var compare = ascending;
- for (var i = 0, f; f = PostEditor.uploadFiles[i]; i++) {
- // Only process image files.
- if (!f.type.match('image.*')) {
- continue;
+ try {
+ set.sort(compare);
+ } catch (e) {
+ return fail(this, INVALID_KEY_ARGUMENT);
}
- var reader = new FileReader(); // Closure to capture the file information.
+ if (set.length === 0) return emptyCollection(this);
+ var c = new Collection(this, function () {
+ return IDBKeyRange.bound(set[0], set[set.length - 1]);
+ });
- reader.onload = function (theFile) {
- return function (f) {
- // Render thumbnail.
- var span = document.createElement('span');
- span.innerHTML = [''].join(''); //document.getElementById('featured-image-drop').insertBefore(span, null);
+ c._ondirectionchange = function (direction) {
+ compare = direction === "next" ? ascending : descending;
+ set.sort(compare);
+ };
- document.getElementById('featured-image-drop').innerHTML = '';
- document.getElementById('featured-image-drop').appendChild(span);
- };
- }(f); // Read in the image file as a data URL.
+ var i = 0;
+ c._addAlgorithm(function (cursor, advance, resolve) {
+ var key = cursor.key;
- reader.readAsDataURL(f);
- }
- }
- }, {
- key: "handlePostImageAdd",
- value: function handlePostImageAdd(e) {
- e.stopPropagation();
- e.preventDefault();
- var self = this;
- var postData = new FormData();
- var files = e.target.files;
+ while (compare(key, set[i]) > 0) {
+ // The cursor has passed beyond this key. Check next.
+ ++i;
- for (var i = 0; i < files.length; i++) {
- var file = files[i]; // Check the file type.
+ if (i === set.length) {
+ // There is no next. Stop searching.
+ advance(resolve);
+ return false;
+ }
+ }
- if (!file.type.match('image.*')) {
- continue;
- }
+ if (compare(key, set[i]) === 0) {
+ // The current cursor value should be included and we should continue a single step in case next item has the same key or possibly our next key in set.
+ return true;
+ } else {
+ // cursor.key not yet at set[i]. Forward cursor to the next key to hunt for.
+ advance(function () {
+ cursor.continue(set[i]);
+ });
+ return false;
+ }
+ });
- postData.append('post_image', file, file.name);
- }
+ return c;
+ },
+ notEqual: function (value) {
+ return this.inAnyRange([[minKey, value], [value, maxKey]], {
+ includeLowers: false,
+ includeUppers: false
+ });
+ },
+ noneOf: function () {
+ var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments);
+ if (set.length === 0) return new Collection(this); // Return entire collection.
- this.dataUtils.request("/api/blog/add-post-image", DataEvent.POST_IMAGE_ADDED, _DataUtils.REQUEST_TYPE_POST, _DataUtils.CONTENT_TYPE_FORM, postData).then(function (response) {
- self.editor.notify(EditorEvent.EDITOR_UPLOAD_POST_IMAGE, JSON.parse(response.request['response']).url);
- }).catch(function (err) {
- console.log(err);
- });
- }
- }]);
+ try {
+ set.sort(ascending);
+ } catch (e) {
+ return fail(this, INVALID_KEY_ARGUMENT);
+ } // Transform ["a","b","c"] to a set of ranges for between/above/below: [[minKey,"a"], ["a","b"], ["b","c"], ["c",maxKey]]
- return PostEditor;
-}();
-exports.default = PostEditor;
-PostEditor.uploadFiles = [];
-},{"../../../../../brain/tools/utilities/DataUtils":"../../../../brain/tools/utilities/DataUtils.js","../../../../../brain/tools/events/DataEvent":"../../../../brain/tools/events/DataEvent.js","../../../../../brain/tools/effects/Animate":"../../../../brain/tools/effects/Animate.js","../actions/PostActions":"actions/PostActions.js","../../../../../brain/tools/events/EditorEvent":"../../../../brain/tools/events/EditorEvent.js","tiny-date-picker":"../../../../node_modules/tiny-date-picker/dist/tiny-date-picker.js","../../../../../brain/tools/utilities/DateUtils":"../../../../brain/tools/utilities/DateUtils.js","../../../../../brain/tools/ui/TextEditor":"../../../../brain/tools/ui/TextEditor.js"}],"controllers/DisplayManager.js":[function(require,module,exports) {
-"use strict";
+ var ranges = set.reduce(function (res, val) {
+ return res ? res.concat([[res[res.length - 1][1], val]]) : [[minKey, val]];
+ }, null);
+ ranges.push([set[set.length - 1], maxKey]);
+ return this.inAnyRange(ranges, {
+ includeLowers: false,
+ includeUppers: false
+ });
+ },
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.default = void 0;
+ /** Filter out values withing given set of ranges.
+ * Example, give children and elders a rebate of 50%:
+ *
+ * db.friends.where('age').inAnyRange([[0,18],[65,Infinity]]).modify({Rebate: 1/2});
+ *
+ * @param {(string|number|Date|Array)[][]} ranges
+ * @param {{includeLowers: boolean, includeUppers: boolean}} options
+ */
+ inAnyRange: function (ranges, options) {
+ if (ranges.length === 0) return emptyCollection(this);
-var _DataUtils = _interopRequireWildcard(require("../../../../../brain/tools/utilities/DataUtils"));
+ if (!ranges.every(function (range) {
+ return range[0] !== undefined && range[1] !== undefined && ascending(range[0], range[1]) <= 0;
+ })) {
+ return fail(this, "First argument to inAnyRange() must be an Array of two-value Arrays [lower,upper] where upper must not be lower than lower", exceptions.InvalidArgument);
+ }
-var _PostEditor = _interopRequireDefault(require("./PostEditor"));
+ var includeLowers = !options || options.includeLowers !== false; // Default to true
-var _Animate = _interopRequireDefault(require("../../../../../brain/tools/effects/Animate"));
+ var includeUppers = options && options.includeUppers === true; // Default to false
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ function addRange(ranges, newRange) {
+ for (var i = 0, l = ranges.length; i < l; ++i) {
+ var range = ranges[i];
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
+ if (cmp(newRange[0], range[1]) < 0 && cmp(newRange[1], range[0]) > 0) {
+ range[0] = min(range[0], newRange[0]);
+ range[1] = max(range[1], newRange[1]);
+ break;
+ }
+ }
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ if (i === l) ranges.push(newRange);
+ return ranges;
+ }
-function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
+ var sortDirection = ascending;
-function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
+ function rangeSorter(a, b) {
+ return sortDirection(a[0], b[0]);
+ } // Join overlapping ranges
-var DisplayManager =
-/*#__PURE__*/
-function () {
- //--------------------------
- // constructor
- //--------------------------
- function DisplayManager() {
- _classCallCheck(this, DisplayManager);
- this.dataUtils = new _DataUtils.default();
- this.currentDisplay = '';
- this.urlPieces = document.URL.split("/"); //grab url so system knows what to display
+ var set;
- this.chooseDisplay(this.urlPieces[5], this.urlPieces[6]);
- } //--------------------------
- // methods
- //--------------------------
+ try {
+ set = ranges.reduce(addRange, []);
+ set.sort(rangeSorter);
+ } catch (ex) {
+ return fail(this, INVALID_KEY_ARGUMENT);
+ }
+ var i = 0;
+ var keyIsBeyondCurrentEntry = includeUppers ? function (key) {
+ return ascending(key, set[i][1]) > 0;
+ } : function (key) {
+ return ascending(key, set[i][1]) >= 0;
+ };
+ var keyIsBeforeCurrentEntry = includeLowers ? function (key) {
+ return descending(key, set[i][0]) > 0;
+ } : function (key) {
+ return descending(key, set[i][0]) >= 0;
+ };
- _createClass(DisplayManager, [{
- key: "start",
- value: function start() {
- var self = this; // new stuff
-
- new _Animate.default().object({
- targets: document.getElementById('loader'),
- duration: 300,
- opacity: 0,
- easing: 'easeInOutQuad',
- complete: function complete() {
- document.getElementById('loader').style.display = 'none';
- document.getElementById('loader').style.visibility = 'hidden';
- new _Animate.default().object({
- targets: document.getElementById('header'),
- duration: 10,
- opacity: 1,
- easing: 'easeInOutQuad',
- complete: function complete() {
- document.getElementById('loader').style.display = 'none';
- document.getElementById('loader').style.visibility = 'hidden';
- }
- });
+ function keyWithinCurrentRange(key) {
+ return !keyIsBeyondCurrentEntry(key) && !keyIsBeforeCurrentEntry(key);
}
- });
- }
- }, {
- key: "chooseDisplay",
- value: function chooseDisplay(section, page) {
- this.currentDisplay = '';
- switch (section) {
- case 'posts':
- this.currentDisplay = new _PostEditor.default();
- break;
+ var checkKey = keyIsBeyondCurrentEntry;
+ var c = new Collection(this, function () {
+ return IDBKeyRange.bound(set[0][0], set[set.length - 1][1], !includeLowers, !includeUppers);
+ });
- default:
- // just chill
- break;
- }
+ c._ondirectionchange = function (direction) {
+ if (direction === "next") {
+ checkKey = keyIsBeyondCurrentEntry;
+ sortDirection = ascending;
+ } else {
+ checkKey = keyIsBeforeCurrentEntry;
+ sortDirection = descending;
+ }
- this.start();
- } //--------------------------
- // event handlers
- //--------------------------
+ set.sort(rangeSorter);
+ };
- }]);
+ c._addAlgorithm(function (cursor, advance, resolve) {
+ var key = cursor.key;
- return DisplayManager;
-}();
+ while (checkKey(key)) {
+ // The cursor has passed beyond this key. Check next.
+ ++i;
+
+ if (i === set.length) {
+ // There is no next. Stop searching.
+ advance(resolve);
+ return false;
+ }
+ }
+
+ if (keyWithinCurrentRange(key)) {
+ // The current cursor value should be included and we should continue a single step in case next item has the same key or possibly our next key in set.
+ return true;
+ } else if (cmp(key, set[i][1]) === 0 || cmp(key, set[i][0]) === 0) {
+ // includeUpper or includeLower is false so keyWithinCurrentRange() returns false even though we are at range border.
+ // Continue to next key but don't include this one.
+ return false;
+ } else {
+ // cursor.key not yet at set[i]. Forward cursor to the next key to hunt for.
+ advance(function () {
+ if (sortDirection === ascending) cursor.continue(set[i][0]);else cursor.continue(set[i][1]);
+ });
+ return false;
+ }
+ });
-exports.default = DisplayManager;
-},{"../../../../../brain/tools/utilities/DataUtils":"../../../../brain/tools/utilities/DataUtils.js","./PostEditor":"controllers/PostEditor.js","../../../../../brain/tools/effects/Animate":"../../../../brain/tools/effects/Animate.js"}],"../../../../node_modules/argsarray/index.js":[function(require,module,exports) {
-'use strict';
+ return c;
+ },
+ startsWithAnyOf: function () {
+ var set = getArrayOf.apply(NO_CHAR_ARRAY, arguments);
-module.exports = argsArray;
+ if (!set.every(function (s) {
+ return typeof s === 'string';
+ })) {
+ return fail(this, "startsWithAnyOf() only works with strings");
+ }
-function argsArray(fun) {
- return function () {
- var len = arguments.length;
- if (len) {
- var args = [];
- var i = -1;
- while (++i < len) {
- args[i] = arguments[i];
+ if (set.length === 0) return emptyCollection(this);
+ return this.inAnyRange(set.map(function (str) {
+ return [str, str + maxString];
+ }));
}
- return fun.call(this, args);
- } else {
- return fun.call(this, []);
- }
- };
-}
-},{}],"../../../../node_modules/immediate/lib/browser.js":[function(require,module,exports) {
-var global = arguments[3];
-'use strict';
-var Mutation = global.MutationObserver || global.WebKitMutationObserver;
-
-var scheduleDrain;
-
-{
- if (Mutation) {
- var called = 0;
- var observer = new Mutation(nextTick);
- var element = global.document.createTextNode('');
- observer.observe(element, {
- characterData: true
- });
- scheduleDrain = function () {
- element.data = (called = ++called % 2);
- };
- } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') {
- var channel = new global.MessageChannel();
- channel.port1.onmessage = nextTick;
- scheduleDrain = function () {
- channel.port2.postMessage(0);
- };
- } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) {
- scheduleDrain = function () {
-
- // Create a