| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- /*
- * Based on github.com/visionmedia/page.js
- * Licensed under the MIT license
- * Copyright 2012 TJ Holowaychuk <tj@vision-media.ca>
- */
- let running = false;
- let currentState = null;
- const callbacks = [];
- this.page = function (value, fn) {
- if (typeof value === "function") {
- page("*", value);
- } else if (typeof fn === "function") {
- const route = new Route(value);
- callbacks.push(route.middleware(fn));
- } else if (typeof value === "string") {
- page.show(value, fn);
- } else {
- page.start(value);
- }
- };
- page.start = function (options) {
- if (options == null) {
- options = {};
- }
- if (!running) {
- running = true;
- addEventListener("popstate", onpopstate);
- addEventListener("click", onclick);
- page.replace(currentPath(), null, null, true);
- }
- };
- page.stop = function () {
- if (running) {
- running = false;
- removeEventListener("click", onclick);
- removeEventListener("popstate", onpopstate);
- }
- };
- page.show = function (path, state) {
- let res;
- if (path === currentState?.path) {
- return;
- }
- const context = new Context(path, state);
- const previousState = currentState;
- currentState = context.state;
- if ((res = page.dispatch(context))) {
- currentState = previousState;
- location.assign(res);
- } else {
- context.pushState();
- updateCanonicalLink();
- track();
- }
- return context;
- };
- page.replace = function (path, state, skipDispatch, init) {
- let result;
- let context = new Context(path, state || currentState);
- context.init = init;
- currentState = context.state;
- if (!skipDispatch) {
- result = page.dispatch(context);
- }
- if (result) {
- context = new Context(result);
- context.init = init;
- currentState = context.state;
- page.dispatch(context);
- }
- context.replaceState();
- updateCanonicalLink();
- if (!skipDispatch) {
- track();
- }
- return context;
- };
- page.dispatch = function (context) {
- let i = 0;
- var next = function () {
- let fn, res;
- if ((fn = callbacks[i++])) {
- res = fn(context, next);
- }
- return res;
- };
- return next();
- };
- page.canGoBack = () => !Context.isIntialState(currentState);
- page.canGoForward = () => !Context.isLastState(currentState);
- const currentPath = () => location.pathname + location.search + location.hash;
- class Context {
- static isIntialState(state) {
- return state.id === 0;
- }
- static isLastState(state) {
- return state.id === this.stateId - 1;
- }
- static isInitialPopState(state) {
- return state.path === this.initialPath && this.stateId === 1;
- }
- static isSameSession(state) {
- return state.sessionId === this.sessionId;
- }
- constructor(path, state) {
- this.initialPath = currentPath();
- this.sessionId = Date.now();
- this.stateId = 0;
- if (path == null) {
- path = "/";
- }
- this.path = path;
- if (state == null) {
- state = {};
- }
- this.state = state;
- this.pathname = this.path.replace(
- /(?:\?([^#]*))?(?:#(.*))?$/,
- (_, query, hash) => {
- this.query = query;
- this.hash = hash;
- return "";
- },
- );
- if (this.state.id == null) {
- this.state.id = this.constructor.stateId++;
- }
- if (this.state.sessionId == null) {
- this.state.sessionId = this.constructor.sessionId;
- }
- this.state.path = this.path;
- }
- pushState() {
- history.pushState(this.state, "", this.path);
- }
- replaceState() {
- try {
- history.replaceState(this.state, "", this.path);
- } catch (error) {} // NS_ERROR_FAILURE in Firefox
- }
- }
- class Route {
- constructor(path, options) {
- this.path = path;
- if (options == null) {
- options = {};
- }
- this.keys = [];
- this.regexp = pathToRegexp(this.path, this.keys);
- }
- middleware(fn) {
- return (context, next) => {
- let params;
- if (this.match(context.pathname, (params = []))) {
- context.params = params;
- return fn(context, next);
- } else {
- return next();
- }
- };
- }
- match(path, params) {
- let matchData;
- if (!(matchData = this.regexp.exec(path))) {
- return;
- }
- const iterable = matchData.slice(1);
- for (let i = 0; i < iterable.length; i++) {
- var key;
- var value = iterable[i];
- if (typeof value === "string") {
- value = decodeURIComponent(value);
- }
- if ((key = this.keys[i])) {
- params[key.name] = value;
- } else {
- params.push(value);
- }
- }
- return true;
- }
- }
- var pathToRegexp = function (path, keys) {
- if (path instanceof RegExp) {
- return path;
- }
- if (path instanceof Array) {
- path = `(${path.join("|")})`;
- }
- path = path
- .replace(/\/\(/g, "(?:/")
- .replace(
- /(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g,
- function (_, slash, format, key, capture, optional) {
- if (slash == null) {
- slash = "";
- }
- if (format == null) {
- format = "";
- }
- keys.push({ name: key, optional: !!optional });
- let str = optional ? "" : slash;
- str += "(?:";
- if (optional) {
- str += slash;
- }
- str += format;
- str += capture || (format ? "([^/.]+?)" : "([^/]+?)");
- str += ")";
- if (optional) {
- str += optional;
- }
- return str;
- },
- )
- .replace(/([\/.])/g, "\\$1")
- .replace(/\*/g, "(.*)");
- return new RegExp(`^${path}$`);
- };
- var onpopstate = function (event) {
- if (!event.state || Context.isInitialPopState(event.state)) {
- return;
- }
- if (Context.isSameSession(event.state)) {
- page.replace(event.state.path, event.state);
- } else {
- location.reload();
- }
- };
- var onclick = function (event) {
- try {
- if (
- event.which !== 1 ||
- event.metaKey ||
- event.ctrlKey ||
- event.shiftKey ||
- event.defaultPrevented
- ) {
- return;
- }
- } catch (error) {
- return;
- }
- let link = $.eventTarget(event);
- while (link && link.tagName !== "A") {
- link = link.parentNode;
- }
- if (link && !link.target && isSameOrigin(link.href)) {
- event.preventDefault();
- let path = link.pathname + link.search + link.hash;
- path = path.replace(/^\/\/+/, "/"); // IE11 bug
- page.show(path);
- }
- };
- var isSameOrigin = (url) =>
- url.startsWith(`${location.protocol}//${location.hostname}`);
- var updateCanonicalLink = function () {
- if (!this.canonicalLink) {
- this.canonicalLink = document.head.querySelector('link[rel="canonical"]');
- }
- return this.canonicalLink.setAttribute(
- "href",
- `https://${location.host}${location.pathname}`,
- );
- };
- const trackers = [];
- page.track = function (fn) {
- trackers.push(fn);
- };
- var track = function () {
- if (app.config.env !== "production") {
- return;
- }
- if (navigator.doNotTrack === "1") {
- return;
- }
- if (navigator.globalPrivacyControl) {
- return;
- }
- const consentGiven = Cookies.get("analyticsConsent");
- const consentAsked = Cookies.get("analyticsConsentAsked");
- if (consentGiven === "1") {
- for (var tracker of trackers) {
- tracker.call();
- }
- } else if (consentGiven === undefined && consentAsked === undefined) {
- // Only ask for consent once per browser session
- Cookies.set("analyticsConsentAsked", "1");
- new app.views.Notif("AnalyticsConsent", { autoHide: null });
- }
- };
- this.resetAnalytics = function () {
- for (var cookie of document.cookie.split(/;\s?/)) {
- var name = cookie.split("=")[0];
- if (name[0] === "_" && name[1] !== "_") {
- Cookies.expire(name);
- }
- }
- };
|