svelte-router-spa
Version:
A full featured router component for Svelte and your Single Page Applications.
1,836 lines (1,605 loc) • 48.9 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function noop() { }
function assign(tar, src) {
// @ts-ignore
for (const k in src)
tar[k] = src[k];
return tar;
}
function run(fn) {
return fn();
}
function blank_object() {
return Object.create(null);
}
function run_all(fns) {
fns.forEach(run);
}
function is_function(thing) {
return typeof thing === 'function';
}
function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}
function is_empty(obj) {
return Object.keys(obj).length === 0;
}
function subscribe(store, ...callbacks) {
if (store == null) {
return noop;
}
const unsub = store.subscribe(...callbacks);
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}
function component_subscribe(component, store, callback) {
component.$$.on_destroy.push(subscribe(store, callback));
}
function create_slot(definition, ctx, $$scope, fn) {
if (definition) {
const slot_ctx = get_slot_context(definition, ctx, $$scope, fn);
return definition[0](slot_ctx);
}
}
function get_slot_context(definition, ctx, $$scope, fn) {
return definition[1] && fn
? assign($$scope.ctx.slice(), definition[1](fn(ctx)))
: $$scope.ctx;
}
function get_slot_changes(definition, $$scope, dirty, fn) {
if (definition[2] && fn) {
const lets = definition[2](fn(dirty));
if ($$scope.dirty === undefined) {
return lets;
}
if (typeof lets === 'object') {
const merged = [];
const len = Math.max($$scope.dirty.length, lets.length);
for (let i = 0; i < len; i += 1) {
merged[i] = $$scope.dirty[i] | lets[i];
}
return merged;
}
return $$scope.dirty | lets;
}
return $$scope.dirty;
}
function update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) {
const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn);
if (slot_changes) {
const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);
slot.p(slot_context, slot_changes);
}
}
function insert(target, node, anchor) {
target.insertBefore(node, anchor || null);
}
function detach(node) {
node.parentNode.removeChild(node);
}
function element(name) {
return document.createElement(name);
}
function text(data) {
return document.createTextNode(data);
}
function empty() {
return text('');
}
function listen(node, event, handler, options) {
node.addEventListener(event, handler, options);
return () => node.removeEventListener(event, handler, options);
}
function attr(node, attribute, value) {
if (value == null)
node.removeAttribute(attribute);
else if (node.getAttribute(attribute) !== value)
node.setAttribute(attribute, value);
}
function children(element) {
return Array.from(element.childNodes);
}
function toggle_class(element, name, toggle) {
element.classList[toggle ? 'add' : 'remove'](name);
}
let current_component;
function set_current_component(component) {
current_component = component;
}
function get_current_component() {
if (!current_component)
throw new Error('Function called outside component initialization');
return current_component;
}
function onMount(fn) {
get_current_component().$$.on_mount.push(fn);
}
const dirty_components = [];
const binding_callbacks = [];
const render_callbacks = [];
const flush_callbacks = [];
const resolved_promise = Promise.resolve();
let update_scheduled = false;
function schedule_update() {
if (!update_scheduled) {
update_scheduled = true;
resolved_promise.then(flush);
}
}
function add_render_callback(fn) {
render_callbacks.push(fn);
}
let flushing = false;
const seen_callbacks = new Set();
function flush() {
if (flushing)
return;
flushing = true;
do {
// first, call beforeUpdate functions
// and update components
for (let i = 0; i < dirty_components.length; i += 1) {
const component = dirty_components[i];
set_current_component(component);
update(component.$$);
}
set_current_component(null);
dirty_components.length = 0;
while (binding_callbacks.length)
binding_callbacks.pop()();
// then, once components are updated, call
// afterUpdate functions. This may cause
// subsequent updates...
for (let i = 0; i < render_callbacks.length; i += 1) {
const callback = render_callbacks[i];
if (!seen_callbacks.has(callback)) {
// ...so guard against infinite loops
seen_callbacks.add(callback);
callback();
}
}
render_callbacks.length = 0;
} while (dirty_components.length);
while (flush_callbacks.length) {
flush_callbacks.pop()();
}
update_scheduled = false;
flushing = false;
seen_callbacks.clear();
}
function update($$) {
if ($$.fragment !== null) {
$$.update();
run_all($$.before_update);
const dirty = $$.dirty;
$$.dirty = [-1];
$$.fragment && $$.fragment.p($$.ctx, dirty);
$$.after_update.forEach(add_render_callback);
}
}
const outroing = new Set();
let outros;
function group_outros() {
outros = {
r: 0,
c: [],
p: outros // parent group
};
}
function check_outros() {
if (!outros.r) {
run_all(outros.c);
}
outros = outros.p;
}
function transition_in(block, local) {
if (block && block.i) {
outroing.delete(block);
block.i(local);
}
}
function transition_out(block, local, detach, callback) {
if (block && block.o) {
if (outroing.has(block))
return;
outroing.add(block);
outros.c.push(() => {
outroing.delete(block);
if (callback) {
if (detach)
block.d(1);
callback();
}
});
block.o(local);
}
}
function create_component(block) {
block && block.c();
}
function mount_component(component, target, anchor) {
const { fragment, on_mount, on_destroy, after_update } = component.$$;
fragment && fragment.m(target, anchor);
// onMount happens before the initial afterUpdate
add_render_callback(() => {
const new_on_destroy = on_mount.map(run).filter(is_function);
if (on_destroy) {
on_destroy.push(...new_on_destroy);
}
else {
// Edge case - component was destroyed immediately,
// most likely as a result of a binding initialising
run_all(new_on_destroy);
}
component.$$.on_mount = [];
});
after_update.forEach(add_render_callback);
}
function destroy_component(component, detaching) {
const $$ = component.$$;
if ($$.fragment !== null) {
run_all($$.on_destroy);
$$.fragment && $$.fragment.d(detaching);
// TODO null out other refs, including component.$$ (but need to
// preserve final state?)
$$.on_destroy = $$.fragment = null;
$$.ctx = [];
}
}
function make_dirty(component, i) {
if (component.$$.dirty[0] === -1) {
dirty_components.push(component);
schedule_update();
component.$$.dirty.fill(0);
}
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));
}
function init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) {
const parent_component = current_component;
set_current_component(component);
const $$ = component.$$ = {
fragment: null,
ctx: null,
// state
props,
update: noop,
not_equal,
bound: blank_object(),
// lifecycle
on_mount: [],
on_destroy: [],
before_update: [],
after_update: [],
context: new Map(parent_component ? parent_component.$$.context : []),
// everything else
callbacks: blank_object(),
dirty,
skip_bound: false
};
let ready = false;
$$.ctx = instance
? instance(component, options.props || {}, (i, ret, ...rest) => {
const value = rest.length ? rest[0] : ret;
if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
if (!$$.skip_bound && $$.bound[i])
$$.bound[i](value);
if (ready)
make_dirty(component, i);
}
return ret;
})
: [];
$$.update();
ready = true;
run_all($$.before_update);
// `false` as a special case of no DOM component
$$.fragment = create_fragment ? create_fragment($$.ctx) : false;
if (options.target) {
if (options.hydrate) {
const nodes = children(options.target);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.l(nodes);
nodes.forEach(detach);
}
else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.c();
}
if (options.intro)
transition_in(component.$$.fragment);
mount_component(component, options.target, options.anchor);
flush();
}
set_current_component(parent_component);
}
/**
* Base class for Svelte components. Used when dev=false.
*/
class SvelteComponent {
$destroy() {
destroy_component(this, 1);
this.$destroy = noop;
}
$on(type, callback) {
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
callbacks.push(callback);
return () => {
const index = callbacks.indexOf(callback);
if (index !== -1)
callbacks.splice(index, 1);
};
}
$set($$props) {
if (this.$$set && !is_empty($$props)) {
this.$$.skip_bound = true;
this.$$set($$props);
this.$$.skip_bound = false;
}
}
}
const subscriber_queue = [];
/**
* Create a `Writable` store that allows both updating and reading by subscription.
* @param {*=}value initial value
* @param {StartStopNotifier=}start start and stop notifications for subscriptions
*/
function writable(value, start = noop) {
let stop;
const subscribers = [];
function set(new_value) {
if (safe_not_equal(value, new_value)) {
value = new_value;
if (stop) { // store is ready
const run_queue = !subscriber_queue.length;
for (let i = 0; i < subscribers.length; i += 1) {
const s = subscribers[i];
s[1]();
subscriber_queue.push(s, value);
}
if (run_queue) {
for (let i = 0; i < subscriber_queue.length; i += 2) {
subscriber_queue[i][0](subscriber_queue[i + 1]);
}
subscriber_queue.length = 0;
}
}
}
}
function update(fn) {
set(fn(value));
}
function subscribe(run, invalidate = noop) {
const subscriber = [run, invalidate];
subscribers.push(subscriber);
if (subscribers.length === 1) {
stop = start(set) || noop;
}
run(value);
return () => {
const index = subscribers.indexOf(subscriber);
if (index !== -1) {
subscribers.splice(index, 1);
}
if (subscribers.length === 0) {
stop();
stop = null;
}
};
}
return { set, update, subscribe };
}
const { set, subscribe: subscribe$1 } = writable({});
const remove = () => {
set({});
};
const activeRoute = {
subscribe: subscribe$1,
set,
remove,
};
const UrlParser = (urlString, namedUrl = '') => {
const urlBase = new URL(urlString);
/**
* Wrapper for URL.hash
*
**/
function hash() {
return urlBase.hash;
}
/**
* Wrapper for URL.host
*
**/
function host() {
return urlBase.host;
}
/**
* Wrapper for URL.hostname
*
**/
function hostname() {
return urlBase.hostname;
}
/**
* Returns an object with all the named params and their values
*
**/
function namedParams() {
const allPathName = pathNames();
const allNamedParamsKeys = namedParamsWithIndex();
return allNamedParamsKeys.reduce((values, paramKey) => {
values[paramKey.value] = allPathName[paramKey.index];
return values;
}, {});
}
/**
* Returns an array with all the named param keys
*
**/
function namedParamsKeys() {
const allNamedParamsKeys = namedParamsWithIndex();
return allNamedParamsKeys.reduce((values, paramKey) => {
values.push(paramKey.value);
return values;
}, []);
}
/**
* Returns an array with all the named param values
*
**/
function namedParamsValues() {
const allPathName = pathNames();
const allNamedParamsKeys = namedParamsWithIndex();
return allNamedParamsKeys.reduce((values, paramKey) => {
values.push(allPathName[paramKey.index]);
return values;
}, []);
}
/**
* Returns an array with all named param ids and their position in the path
* Private
**/
function namedParamsWithIndex() {
const namedUrlParams = getPathNames(namedUrl);
return namedUrlParams.reduce((validParams, param, index) => {
if (param[0] === ':') {
validParams.push({ value: param.slice(1), index });
}
return validParams;
}, []);
}
/**
* Wrapper for URL.port
*
**/
function port() {
return urlBase.port;
}
/**
* Wrapper for URL.pathname
*
**/
function pathname() {
return urlBase.pathname;
}
/**
* Wrapper for URL.protocol
*
**/
function protocol() {
return urlBase.protocol;
}
/**
* Wrapper for URL.search
*
**/
function search() {
return urlBase.search;
}
/**
* Returns an object with all query params and their values
*
**/
function queryParams() {
const params = {};
urlBase.searchParams.forEach((value, key) => {
params[key] = value;
});
return params;
}
/**
* Returns an array with all the query param keys
*
**/
function queryParamsKeys() {
const params = [];
urlBase.searchParams.forEach((_value, key) => {
params.push(key);
});
return params;
}
/**
* Returns an array with all the query param values
*
**/
function queryParamsValues() {
const params = [];
urlBase.searchParams.forEach((value) => {
params.push(value);
});
return params;
}
/**
* Returns an array with all the elements of a pathname
*
**/
function pathNames() {
return getPathNames(urlBase.pathname);
}
/**
* Returns an array with all the parts of a pathname
* Private method
**/
function getPathNames(pathName) {
if (pathName === '/' || pathName.trim().length === 0) return [pathName];
if (pathName.slice(-1) === '/') {
pathName = pathName.slice(0, -1);
}
if (pathName[0] === '/') {
pathName = pathName.slice(1);
}
return pathName.split('/');
}
return Object.freeze({
hash: hash(),
host: host(),
hostname: hostname(),
namedParams: namedParams(),
namedParamsKeys: namedParamsKeys(),
namedParamsValues: namedParamsValues(),
pathNames: pathNames(),
port: port(),
pathname: pathname(),
protocol: protocol(),
search: search(),
queryParams: queryParams(),
queryParamsKeys: queryParamsKeys(),
queryParamsValues: queryParamsValues(),
});
};
/**
* Returns true if object has any nested routes empty
* @param routeObject
**/
const anyEmptyNestedRoutes = (routeObject) => {
let result = false;
if (Object.keys(routeObject).length === 0) {
return true;
}
if (routeObject.childRoute && Object.keys(routeObject.childRoute).length === 0) {
result = true;
} else if (routeObject.childRoute) {
result = anyEmptyNestedRoutes(routeObject.childRoute);
}
return result;
};
/**
* Compare two routes ignoring named params
* @param pathName string
* @param routeName string
**/
const compareRoutes = (pathName, routeName) => {
routeName = removeSlash(routeName);
if (routeName.includes(':')) {
return routeName.includes(pathName);
} else {
return routeName.startsWith(pathName);
}
};
/**
* Returns a boolean indicating if the name of path exists in the route based on the language parameter
* @param pathName string
* @param route object
* @param language string
**/
const findLocalisedRoute = (pathName, route, language) => {
let exists = false;
if (language) {
return { exists: route.lang && route.lang[language] && route.lang[language].includes(pathName), language };
}
exists = compareRoutes(pathName, route.name);
if (!exists && route.lang && typeof route.lang === 'object') {
for (const [key, value] of Object.entries(route.lang)) {
if (compareRoutes(pathName, value)) {
exists = true;
language = key;
}
}
}
return { exists, language };
};
/**
* Return all the consecutive named param (placeholders) of a pathname
* @param pathname
**/
const getNamedParams = (pathName = '') => {
if (pathName.trim().length === 0) return [];
const namedUrlParams = getPathNames(pathName);
return namedUrlParams.reduce((validParams, param) => {
if (param[0] === ':') {
validParams.push(param.slice(1));
}
return validParams;
}, []);
};
/**
* Split a pathname based on /
* @param pathName
* Private method
**/
const getPathNames = (pathName) => {
if (pathName === '/' || pathName.trim().length === 0) return [pathName];
pathName = removeSlash(pathName, 'both');
return pathName.split('/');
};
/**
* Return the first part of a pathname until the first named param is found
* @param name
**/
const nameToPath = (name = '') => {
let routeName;
if (name === '/' || name.trim().length === 0) return name;
name = removeSlash(name, 'lead');
routeName = name.split(':')[0];
routeName = removeSlash(routeName, 'trail');
return routeName.toLowerCase();
};
/**
* Return the path name excluding query params
* @param name
**/
const pathWithoutQueryParams = (currentRoute) => {
const path = currentRoute.path.split('?');
return path[0];
};
/**
* Return the path name including query params
* @param name
**/
const pathWithQueryParams = (currentRoute) => {
let queryParams = [];
if (currentRoute.queryParams) {
for (let [key, value] of Object.entries(currentRoute.queryParams)) {
queryParams.push(`${key}=${value}`);
}
}
const hash = currentRoute.hash ? currentRoute.hash : '';
if (queryParams.length > 0) {
return `${currentRoute.path}?${queryParams.join('&')}${hash}`;
} else {
return currentRoute.path + hash;
}
};
/**
* Returns a string with trailing or leading slash character removed
* @param pathName string
* @param position string - lead, trail, both
**/
const removeExtraPaths = (pathNames, basePathNames) => {
const names = basePathNames.split('/');
if (names.length > 1) {
names.forEach(function (name, index) {
if (name.length > 0 && index > 0) {
pathNames.shift();
}
});
}
return pathNames;
};
/**
* Returns a string with trailing or leading slash character removed
* @param pathName string
* @param position string - lead, trail, both
**/
const removeSlash = (pathName, position = 'lead') => {
if (pathName.trim().length < 1) {
return '';
}
if (position === 'trail' || position === 'both') {
if (pathName.slice(-1) === '/') {
pathName = pathName.slice(0, -1);
}
}
if (position === 'lead' || position === 'both') {
if (pathName[0] === '/') {
pathName = pathName.slice(1);
}
}
return pathName;
};
/**
* Returns the name of the route based on the language parameter
* @param route object
* @param language string
**/
const routeNameLocalised = (route, language = null) => {
if (!language || !route.lang || !route.lang[language]) {
return route.name;
} else {
return route.lang[language];
}
};
/**
* Return the path name excluding query params
* @param name
**/
const startsWithNamedParam = (currentRoute) => {
const routeName = removeSlash(currentRoute);
return routeName.startsWith(':');
};
/**
* Updates the base route path.
* Route objects can have nested routes (childRoutes) or just a long name like "admin/employees/show/:id"
*
* @param basePath string
* @param pathNames array
* @param route object
* @param language string
**/
const updateRoutePath = (basePath, pathNames, route, language, convert = false) => {
if (basePath === '/' || basePath.trim().length === 0) return { result: basePath, language: null };
let basePathResult = basePath;
let routeName = route.name;
let currentLanguage = language;
if (convert) {
currentLanguage = '';
}
routeName = removeSlash(routeName);
basePathResult = removeSlash(basePathResult);
if (!route.childRoute) {
let localisedRoute = findLocalisedRoute(basePathResult, route, currentLanguage);
if (localisedRoute.exists && convert) {
basePathResult = routeNameLocalised(route, language);
}
let routeNames = routeName.split(':')[0];
routeNames = removeSlash(routeNames, 'trail');
routeNames = routeNames.split('/');
routeNames.shift();
routeNames.forEach(() => {
const currentPathName = pathNames[0];
localisedRoute = findLocalisedRoute(`${basePathResult}/${currentPathName}`, route, currentLanguage);
if (currentPathName && localisedRoute.exists) {
if (convert) {
basePathResult = routeNameLocalised(route, language);
} else {
basePathResult = `${basePathResult}/${currentPathName}`;
}
pathNames.shift();
} else {
return { result: basePathResult, language: localisedRoute.language };
}
});
return { result: basePathResult, language: localisedRoute.language };
} else {
return { result: basePath, language: currentLanguage };
}
};
const RouterCurrent = (trackPage) => {
const trackPageview = trackPage || false;
let activeRoute = '';
const setActive = (newRoute, updateBrowserHistory) => {
activeRoute = newRoute.path;
pushActiveRoute(newRoute, updateBrowserHistory);
};
const active = () => {
return activeRoute;
};
/**
* Returns true if pathName is current active route
* @param pathName String The path name to check against the current route.
* @param includePath Boolean if true checks that pathName is included in current route. If false should match it.
**/
const isActive = (queryPath, includePath = false) => {
if (queryPath[0] !== '/') {
queryPath = '/' + queryPath;
}
// remove query params for comparison
let pathName = UrlParser(`http://fake.com${queryPath}`).pathname;
let activeRoutePath = UrlParser(`http://fake.com${activeRoute}`).pathname;
pathName = removeSlash(pathName, 'trail');
activeRoutePath = removeSlash(activeRoutePath, 'trail');
if (includePath) {
return activeRoutePath.includes(pathName);
} else {
return activeRoutePath === pathName;
}
};
const pushActiveRoute = (newRoute, updateBrowserHistory) => {
if (typeof window !== 'undefined') {
const pathAndSearch = pathWithQueryParams(newRoute);
if (updateBrowserHistory) {
window.history.pushState({ page: pathAndSearch }, '', pathAndSearch);
}
// Moving back in history does not update browser history but does update tracking.
if (trackPageview) {
gaTracking(pathAndSearch);
}
}
};
const gaTracking = (newPage) => {
if (typeof ga !== 'undefined') {
ga('set', 'page', newPage);
ga('send', 'pageview');
}
};
return Object.freeze({ active, isActive, setActive });
};
const RouterGuard = (onlyIf) => {
const guardInfo = onlyIf;
const valid = () => {
return guardInfo && guardInfo.guard && typeof guardInfo.guard === 'function';
};
const redirect = () => {
return !guardInfo.guard();
};
const redirectPath = () => {
let destinationUrl = '/';
if (guardInfo.redirect && guardInfo.redirect.length > 0) {
destinationUrl = guardInfo.redirect;
}
return destinationUrl;
};
return Object.freeze({ valid, redirect, redirectPath });
};
const RouterRedirect = (route, currentPath) => {
const guard = RouterGuard(route.onlyIf);
const path = () => {
let redirectTo = currentPath;
if (route.redirectTo && route.redirectTo.length > 0) {
redirectTo = route.redirectTo;
}
if (guard.valid() && guard.redirect()) {
redirectTo = guard.redirectPath();
}
return redirectTo;
};
return Object.freeze({ path });
};
function RouterRoute({ routeInfo, path, routeNamedParams, urlParser, namedPath, language }) {
const namedParams = () => {
const parsedParams = UrlParser(`https://fake.com${urlParser.pathname}`, namedPath).namedParams;
return { ...routeNamedParams, ...parsedParams };
};
const get = () => {
return {
name: path,
component: routeInfo.component,
hash: urlParser.hash,
layout: routeInfo.layout,
queryParams: urlParser.queryParams,
namedParams: namedParams(),
path,
language,
};
};
return Object.freeze({ get, namedParams });
}
function RouterPath({ basePath, basePathName, pathNames, convert, currentLanguage }) {
let updatedPathRoute;
let route;
let routePathLanguage = currentLanguage;
function updatedPath(currentRoute) {
route = currentRoute;
updatedPathRoute = updateRoutePath(basePathName, pathNames, route, routePathLanguage, convert);
routePathLanguage = convert ? currentLanguage : updatedPathRoute.language;
return updatedPathRoute;
}
function localisedPathName() {
return routeNameLocalised(route, routePathLanguage);
}
function localisedRouteWithoutNamedParams() {
return nameToPath(localisedPathName());
}
function basePathNameWithoutNamedParams() {
return nameToPath(updatedPathRoute.result);
}
function namedPath() {
const localisedPath = localisedPathName();
return basePath ? `${basePath}/${localisedPath}` : localisedPath;
}
function routePath() {
let routePathValue = `${basePath}/${basePathNameWithoutNamedParams()}`;
if (routePathValue === '//') {
routePathValue = '/';
}
if (routePathLanguage) {
pathNames = removeExtraPaths(pathNames, localisedRouteWithoutNamedParams());
}
const namedParams = getNamedParams(localisedPathName());
if (namedParams && namedParams.length > 0) {
namedParams.forEach(function () {
if (pathNames.length > 0) {
routePathValue += `/${pathNames.shift()}`;
}
});
}
return routePathValue;
}
function routeLanguage() {
return routePathLanguage;
}
function basePathSameAsLocalised() {
return basePathNameWithoutNamedParams() === localisedRouteWithoutNamedParams();
}
return Object.freeze({
basePathSameAsLocalised,
updatedPath,
basePathNameWithoutNamedParams,
localisedPathName,
localisedRouteWithoutNamedParams,
namedPath,
pathNames,
routeLanguage,
routePath,
});
}
const NotFoundPage = '/404.html';
function RouterFinder({ routes, currentUrl, routerOptions, convert }) {
const defaultLanguage = routerOptions.defaultLanguage;
const sitePrefix = routerOptions.prefix ? routerOptions.prefix.toLowerCase() : '';
const urlParser = parseCurrentUrl(currentUrl, sitePrefix);
let redirectTo = '';
let routeNamedParams = {};
let staticParamMatch = false;
function findActiveRoute() {
let searchActiveRoute = searchActiveRoutes(routes, '', urlParser.pathNames, routerOptions.lang, convert);
if (!searchActiveRoute || !Object.keys(searchActiveRoute).length || anyEmptyNestedRoutes(searchActiveRoute)) {
if (typeof window !== 'undefined') {
searchActiveRoute = routeNotFound(routerOptions.lang);
}
} else {
searchActiveRoute.path = pathWithoutQueryParams(searchActiveRoute);
if (sitePrefix) {
searchActiveRoute.path = `/${sitePrefix}${searchActiveRoute.path}`;
}
}
return searchActiveRoute;
}
/**
* Gets an array of routes and the browser pathname and return the active route
* @param routes
* @param basePath
* @param pathNames
**/
function searchActiveRoutes(routes, basePath, pathNames, currentLanguage, convert) {
let currentRoute = {};
let basePathName = pathNames.shift().toLowerCase();
const routerPath = RouterPath({ basePath, basePathName, pathNames, convert, currentLanguage });
staticParamMatch = false;
routes.forEach(function (route) {
routerPath.updatedPath(route);
if (matchRoute(routerPath, route.name)) {
let routePath = routerPath.routePath();
redirectTo = RouterRedirect(route, redirectTo).path();
if (currentRoute.name !== routePath) {
currentRoute = setCurrentRoute({
route,
routePath,
routeLanguage: routerPath.routeLanguage(),
urlParser,
namedPath: routerPath.namedPath(),
});
}
if (route.nestedRoutes && route.nestedRoutes.length > 0 && routerPath.pathNames.length > 0) {
currentRoute.childRoute = searchActiveRoutes(
route.nestedRoutes,
routePath,
routerPath.pathNames,
routerPath.routeLanguage(),
convert
);
currentRoute.path = currentRoute.childRoute.path;
currentRoute.language = currentRoute.childRoute.language;
} else if (nestedRoutesAndNoPath(route, routerPath.pathNames)) {
const indexRoute = searchActiveRoutes(
route.nestedRoutes,
routePath,
['index'],
routerPath.routeLanguage(),
convert
);
if (indexRoute && Object.keys(indexRoute).length > 0) {
currentRoute.childRoute = indexRoute;
currentRoute.language = currentRoute.childRoute.language;
}
}
}
});
if (redirectTo) {
currentRoute.redirectTo = redirectTo;
}
return currentRoute;
}
function matchRoute(routerPath, routeName) {
const basePathSameAsLocalised = routerPath.basePathSameAsLocalised();
if (basePathSameAsLocalised) {
staticParamMatch = true;
}
return basePathSameAsLocalised || (!staticParamMatch && startsWithNamedParam(routeName));
}
function nestedRoutesAndNoPath(route, pathNames) {
return route.nestedRoutes && route.nestedRoutes.length > 0 && pathNames.length === 0;
}
function parseCurrentUrl(currentUrl, sitePrefix) {
if (sitePrefix && sitePrefix.trim().length > 0) {
const noPrefixUrl = currentUrl.replace(sitePrefix + '/', '');
return UrlParser(noPrefixUrl);
} else {
return UrlParser(currentUrl);
}
}
function setCurrentRoute({ route, routePath, routeLanguage, urlParser, namedPath }) {
const routerRoute = RouterRoute({
routeInfo: route,
urlParser,
path: routePath,
routeNamedParams,
namedPath,
language: routeLanguage || defaultLanguage,
});
routeNamedParams = routerRoute.namedParams();
return routerRoute.get();
}
const routeNotFound = (customLanguage) => {
const custom404Page = routes.find((route) => route.name == '404');
const language = customLanguage || defaultLanguage || '';
if (custom404Page) {
return { ...custom404Page, language, path: '404' };
} else {
return { name: '404', component: '', path: '404', redirectTo: NotFoundPage };
}
};
return Object.freeze({ findActiveRoute });
}
const NotFoundPage$1 = '/404.html';
let userDefinedRoutes = [];
let routerOptions = {};
let routerCurrent;
/**
* Object exposes one single property: activeRoute
* @param routes Array of routes
* @param currentUrl current url
* @param options configuration options
**/
const SpaRouter = (routes, currentUrl, options = {}) => {
routerOptions = { ...options };
if (typeof currentUrl === 'undefined' || currentUrl === '') {
currentUrl = document.location.href;
}
routerCurrent = RouterCurrent(routerOptions.gaPageviews);
currentUrl = removeSlash(currentUrl, 'trail');
userDefinedRoutes = routes;
const findActiveRoute = () => {
let convert = false;
if (routerOptions.langConvertTo) {
routerOptions.lang = routerOptions.langConvertTo;
convert = true;
}
return RouterFinder({ routes, currentUrl, routerOptions, convert }).findActiveRoute();
};
/**
* Redirect current route to another
* @param destinationUrl
**/
const navigateNow = (destinationUrl, updateBrowserHistory) => {
if (typeof window !== 'undefined') {
if (destinationUrl === NotFoundPage$1) {
routerCurrent.setActive({ path: NotFoundPage$1 }, updateBrowserHistory);
} else {
navigateTo(destinationUrl);
}
}
return destinationUrl;
};
const setActiveRoute = (updateBrowserHistory = true) => {
const currentRoute = findActiveRoute();
if (currentRoute.redirectTo) {
return navigateNow(currentRoute.redirectTo, updateBrowserHistory);
}
routerCurrent.setActive(currentRoute, updateBrowserHistory);
activeRoute.set(currentRoute);
return currentRoute;
};
return Object.freeze({
setActiveRoute,
findActiveRoute,
});
};
/**
* Converts a route to its localised version
* @param pathName
**/
const localisedRoute = (pathName, language) => {
pathName = removeSlash(pathName, 'lead');
routerOptions.langConvertTo = language;
return SpaRouter(userDefinedRoutes, 'http://fake.com/' + pathName, routerOptions).findActiveRoute();
};
/**
* Updates the current active route and updates the browser pathname
* @param pathName String
* @param language String
* @param updateBrowserHistory Boolean
**/
const navigateTo = (pathName, language = null, updateBrowserHistory = true) => {
pathName = removeSlash(pathName, 'lead');
if (language) {
routerOptions.langConvertTo = language;
}
return SpaRouter(userDefinedRoutes, 'http://fake.com/' + pathName, routerOptions).setActiveRoute(
updateBrowserHistory
);
};
/**
* Returns true if pathName is current active route
* @param pathName String The path name to check against the current route.
* @param includePath Boolean if true checks that pathName is included in current route. If false should match it.
**/
const routeIsActive = (queryPath, includePath = false) => {
return routerCurrent.isActive(queryPath, includePath);
};
if (typeof window !== 'undefined') {
// Avoid full page reload on local routes
window.addEventListener('click', (event) => {
if (event.target.localName.toLowerCase() !== 'a') return;
if (event.metaKey || event.ctrlKey || event.shiftKey) return;
const sitePrefix = routerOptions.prefix ? `/${routerOptions.prefix.toLowerCase()}` : '';
const targetHostNameInternal = event.target.pathname && event.target.host === window.location.host;
const prefixMatchPath = sitePrefix.length > 1 ? event.target.pathname.startsWith(sitePrefix) : true;
if (targetHostNameInternal && prefixMatchPath) {
event.preventDefault();
let navigatePathname = event.target.pathname + event.target.search;
const destinationUrl = navigatePathname + event.target.search + event.target.hash;
if (event.target.target === '_blank') {
window.open(destinationUrl, 'newTab');
} else {
navigateTo(destinationUrl);
}
}
});
window.onpopstate = function (_event) {
let navigatePathname = window.location.pathname + window.location.search + window.location.hash;
navigateTo(navigatePathname, null, false);
};
}
/* src/components/route.svelte generated by Svelte v3.32.3 */
function create_if_block_2(ctx) {
let route;
let current;
route = new Route({
props: {
currentRoute: /*currentRoute*/ ctx[0].childRoute,
params: /*params*/ ctx[1]
}
});
return {
c() {
create_component(route.$$.fragment);
},
m(target, anchor) {
mount_component(route, target, anchor);
current = true;
},
p(ctx, dirty) {
const route_changes = {};
if (dirty & /*currentRoute*/ 1) route_changes.currentRoute = /*currentRoute*/ ctx[0].childRoute;
if (dirty & /*params*/ 2) route_changes.params = /*params*/ ctx[1];
route.$set(route_changes);
},
i(local) {
if (current) return;
transition_in(route.$$.fragment, local);
current = true;
},
o(local) {
transition_out(route.$$.fragment, local);
current = false;
},
d(detaching) {
destroy_component(route, detaching);
}
};
}
// (8:33)
function create_if_block_1(ctx) {
let switch_instance;
let switch_instance_anchor;
let current;
var switch_value = /*currentRoute*/ ctx[0].component;
function switch_props(ctx) {
return {
props: {
currentRoute: {
.../*currentRoute*/ ctx[0],
component: ""
},
params: /*params*/ ctx[1]
}
};
}
if (switch_value) {
switch_instance = new switch_value(switch_props(ctx));
}
return {
c() {
if (switch_instance) create_component(switch_instance.$$.fragment);
switch_instance_anchor = empty();
},
m(target, anchor) {
if (switch_instance) {
mount_component(switch_instance, target, anchor);
}
insert(target, switch_instance_anchor, anchor);
current = true;
},
p(ctx, dirty) {
const switch_instance_changes = {};
if (dirty & /*currentRoute*/ 1) switch_instance_changes.currentRoute = {
.../*currentRoute*/ ctx[0],
component: ""
};
if (dirty & /*params*/ 2) switch_instance_changes.params = /*params*/ ctx[1];
if (switch_value !== (switch_value = /*currentRoute*/ ctx[0].component)) {
if (switch_instance) {
group_outros();
const old_component = switch_instance;
transition_out(old_component.$$.fragment, 1, 0, () => {
destroy_component(old_component, 1);
});
check_outros();
}
if (switch_value) {
switch_instance = new switch_value(switch_props(ctx));
create_component(switch_instance.$$.fragment);
transition_in(switch_instance.$$.fragment, 1);
mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
} else {
switch_instance = null;
}
} else if (switch_value) {
switch_instance.$set(switch_instance_changes);
}
},
i(local) {
if (current) return;
if (switch_instance) transition_in(switch_instance.$$.fragment, local);
current = true;
},
o(local) {
if (switch_instance) transition_out(switch_instance.$$.fragment, local);
current = false;
},
d(detaching) {
if (detaching) detach(switch_instance_anchor);
if (switch_instance) destroy_component(switch_instance, detaching);
}
};
}
// (6:0) {#if currentRoute.layout}
function create_if_block(ctx) {
let switch_instance;
let switch_instance_anchor;
let current;
var switch_value = /*currentRoute*/ ctx[0].layout;
function switch_props(ctx) {
return {
props: {
currentRoute: { .../*currentRoute*/ ctx[0], layout: "" },
params: /*params*/ ctx[1]
}
};
}
if (switch_value) {
switch_instance = new switch_value(switch_props(ctx));
}
return {
c() {
if (switch_instance) create_component(switch_instance.$$.fragment);
switch_instance_anchor = empty();
},
m(target, anchor) {
if (switch_instance) {
mount_component(switch_instance, target, anchor);
}
insert(target, switch_instance_anchor, anchor);
current = true;
},
p(ctx, dirty) {
const switch_instance_changes = {};
if (dirty & /*currentRoute*/ 1) switch_instance_changes.currentRoute = { .../*currentRoute*/ ctx[0], layout: "" };
if (dirty & /*params*/ 2) switch_instance_changes.params = /*params*/ ctx[1];
if (switch_value !== (switch_value = /*currentRoute*/ ctx[0].layout)) {
if (switch_instance) {
group_outros();
const old_component = switch_instance;
transition_out(old_component.$$.fragment, 1, 0, () => {
destroy_component(old_component, 1);
});
check_outros();
}
if (switch_value) {
switch_instance = new switch_value(switch_props(ctx));
create_component(switch_instance.$$.fragment);
transition_in(switch_instance.$$.fragment, 1);
mount_component(switch_instance, switch_instance_anchor.parentNode, switch_instance_anchor);
} else {
switch_instance = null;
}
} else if (switch_value) {
switch_instance.$set(switch_instance_changes);
}
},
i(local) {
if (current) return;
if (switch_instance) transition_in(switch_instance.$$.fragment, local);
current = true;
},
o(local) {
if (switch_instance) transition_out(switch_instance.$$.fragment, local);
current = false;
},
d(detaching) {
if (detaching) detach(switch_instance_anchor);
if (switch_instance) destroy_component(switch_instance, detaching);
}
};
}
function create_fragment(ctx) {
let current_block_type_index;
let if_block;
let if_block_anchor;
let current;
const if_block_creators = [create_if_block, create_if_block_1, create_if_block_2];
const if_blocks = [];
function select_block_type(ctx, dirty) {
if (/*currentRoute*/ ctx[0].layout) return 0;
if (/*currentRoute*/ ctx[0].component) return 1;
if (/*currentRoute*/ ctx[0].childRoute) return 2;
return -1;
}
if (~(current_block_type_index = select_block_type(ctx))) {
if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
}
return {
c() {
if (if_block) if_block.c();
if_block_anchor = empty();
},
m(target, anchor) {
if (~current_block_type_index) {
if_blocks[current_block_type_index].m(target, anchor);
}
insert(target, if_block_anchor, anchor);
current = true;
},
p(ctx, [dirty]) {
let previous_block_index = current_block_type_index;
current_block_type_index = select_block_type(ctx);
if (current_block_type_index === previous_block_index) {
if (~current_block_type_index) {
if_blocks[current_block_type_index].p(ctx, dirty);
}
} else {
if (if_block) {
group_outros();
transition_out(if_blocks[previous_block_index], 1, 1, () => {
if_blocks[previous_block_index] = null;
});
check_outros();
}
if (~current_block_type_index) {
if_block = if_blocks[current_block_type_index];
if (!if_block) {
if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
if_block.c();
} else {
if_block.p(ctx, dirty);
}
transition_in(if_block, 1);
if_block.m(if_block_anchor.parentNode, if_block_anchor);
} else {
if_block = null;
}
}
},
i(local) {
if (current) return;
transition_in(if_block);
current = true;
},
o(local) {
transition_out(if_block);
current = false;
},
d(detaching) {
if (~current_block_type_index) {
if_blocks[current_block_type_index].d(detaching);
}
if (detaching) detach(if_block_anchor);
}
};
}
function instance($$self, $$props, $$invalidate) {
let { currentRoute = {} } = $$props;
let { params = {} } = $$props;
$$self.$$set = $$props => {
if ("currentRoute" in $$props) $$invalidate(0, currentRoute = $$props.currentRoute);
if ("params" in $$props) $$invalidate(1, params = $$props.params);
};
return [currentRoute, params];
}
class Route extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, { currentRoute: 0, params: 1 });
}
}
/* src/components/router.svelte generated by Svelte v3.32.3 */
function create_fragment$1(ctx) {
let route;
let current;
route = new Route({
props: { currentRoute: /*$activeRoute*/ ctx[0] }
});
return {
c() {
create_component(route.$$.fragment);
},
m(target, anchor) {
mount_component(route, target, anchor);
current = true;
},
p(ctx, [dirty]) {
const route_changes = {};
if (dirty & /*$activeRoute*/ 1) route_changes.currentRoute = /*$activeRoute*/ ctx[0];
route.$set(route_changes);
},
i(local) {
if (current) return;
transition_in(route.$$.fragment, local);
current = true;
},
o(local) {
transition_out(route.$$.fragment, local);
current = false;
},
d(detaching) {
destroy_component(route, detaching);
}
};
}
function instance$1($$self, $$props, $$invalidate) {
let $activeRoute;
component_subscribe($$self, activeRoute, $$value => $$invalidate(0, $activeRoute = $$value));
let { routes = [] } = $$props;
let { options = {} } = $$props;
onMount(() => {
SpaRouter(routes, document.location.href, options).setActiveRoute();
});
$$self.$$set = $$props => {
if ("routes" in $$props) $$invalidate(1, routes = $$props.routes);
if ("options" in $$props) $$invalidate(2, options = $$props.options);
};
return [$activeRoute, routes, options];
}
class Router extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$1, create_fragment$1, safe_not_equal, { routes: 1, options: 2 });
}
}
/* src/components/navigate.svelte generated by Svelte v3.32.3 */
function create_fragment$2(ctx) {
let a;
let current;
let mounted;
let dispose;
const default_slot_template = /*#slots*/ ctx[6].default;
const default_slot = create_slot(default_slot_template, ctx, /*$$scope*/ ctx[5], null);
return {
c() {
a = element("a");
if (default_slot) default_slot.c();
attr(a, "href", /*to*/ ctx[0]);
attr(a, "title", /*title*/ ctx[1]);
attr(a, "class", /*styles*/ ctx[2]);
toggle_class(a, "active", routeIsActive(/*to*/ ctx[0]));
},
m(target, anchor) {
insert(target, a, anchor);
if (default_slot) {
default_slot.m(a, null);
}
current = true;
if (!mounted) {
dispose = listen(a, "click", /*navigate*/ ctx[3]);
mounted = true;
}
},
p(ctx, [dirty]) {
if (default_slot) {
if (default_slot.p && dirty & /*$$scope*/ 32) {
update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[5], dirty, null, null);
}
}
if (!current || dirty & /*to*/ 1) {
attr(a, "href", /*to*/ ctx[0]);
}
if (!current || dirty & /*title*/ 2) {
attr(a, "title", /*title*/ ctx[1]);
}
if (!current || dirty & /*styles*/ 4) {
attr(a, "class", /*styles*/ ctx[2]);
}
if (dirty & /*styles, routeIsActive, to*/ 5) {
toggle_class(a, "active", routeIsActive(/*to*/ ctx[0]));
}
},
i(local) {
if (current) return;
transition_in(default_slot, local);
current = true;
},
o(local) {
transition_out(default_slot, local);
current = false;
},
d(detaching) {
if (detaching) detach(a);
if (default_slot) default_slot.d(detaching);
mounted = false;
dispose();
}
};
}
function instance$2($$self, $$props, $$invalidate) {
let { $$slots: slots = {}, $$scope } = $$props;
let { to = "/" } = $$props;
let { title = "" } = $$props;
let { styles = "" } = $$props;
let { lang = null } = $$props;
onMount(() => {
if (lang) {
const route = localisedRoute(to, lang);
if (route) {
$$invalidate(0, to = route.path);
}
}
});
const navigate = event => {
if (event.metaKey || event.ctrlKey || event.shiftKey) return;
event.preventDefault();
event.stopPropagation();
navigateTo(to);
};
$$self.$$set = $$props => {
if ("to" in $$props) $$invalidate(0, to = $$props.to);
if ("title" in $$props) $$invalidate(1, title = $$props.title);
if ("styles" in $$props) $$invalidate(2, styles = $$props.styles);
if ("lang" in $$props) $$invalidate(4, lang = $$props.lang);
if ("$$scope" in $$props) $$invalidate(5, $$scope = $$props.$$scope);
};
return [to, title, styles, navigate, lang, $$scope, slots];
}
class Navigate extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance$2, create_fragment$2, safe_not_equal, { to: 0, title: 1, styles: 2, lang: 4 });
}
}
exports.Navigate = Navigate;
exports.Route = Route;
exports.Router = Router;
exports.SpaRouter = SpaRouter;
exports.localisedRoute = localisedRoute;
exports.navigateTo = navigateTo;
exports.routeIsActive = routeIsActive;