koco
Version:
knockout components with routing
233 lines (189 loc) • 7.37 kB
JavaScript
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(['exports'], factory);
} else if (typeof exports !== "undefined") {
factory(exports);
} else {
var mod = {
exports: {}
};
factory(mod.exports);
global.routerStatePush = mod.exports;
}
})(this, function (exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var _createClass = function () {
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 function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
// Copyright (c) CBC/Radio-Canada. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// https://developer.mozilla.org/en-US/docs/Web/API/Location
// http://medialize.github.io/URI.js/about-uris.html
// TODO: Supporter les urls complètes (on supporte relative seulement en ce moement).
// Pour, exemple, pemettre de naviguer dans un sous-domain dans la même app...
var tagNames = ['A', 'AREA'];
function getClickableElement(clckedElement /*, tagNames */) {
if (tagNames.indexOf(clckedElement.tagName) > -1) {
return clckedElement;
}
var cursor = clckedElement;
while (cursor.parentNode) {
if (tagNames.indexOf(cursor.tagName) > -1) {
return cursor;
}
cursor = cursor.parentNode;
}
return null;
}
function backAndForwardButtonHandler(self, e) {
// why this if???
// if (e.state !== null) {
self.backOrForwardDebounced(e.state);
// }
}
// todo: rendre overridable (configuration)?
function shouldHandleNavigation(self, url) {
return url.toLowerCase().startsWith(self.router.settings.baseUrl.toLowerCase());
}
function startsWithRootUrl(self, url) {
return url.toLowerCase().startsWith((document.location.origin + self.router.settings.baseUrl).toLowerCase());
}
function getRelativeUrl(url) {
return '/' + url.replace(/^(?:\/\/|[^\/]+)*\//, '');
}
function hrefClickHandler(self, e, clickableElement) {
// Only handle left-click with no modifiers
if (e.which !== 1 || e.shiftKey || e.altKey || e.metaKey || e.ctrlKey) {
return;
}
var ignore = clickableElement.getAttribute('data-router-ignore');
if (ignore) {
return;
}
var url = clickableElement.getAttribute('href');
// TODO: permettre un regex (ou autre) en config pour savoir si c'est un lien interne
// car avec ça les sous-domaines vont etre exclus
// ce qui ne doit pas nécessairement etre le cas!
var isRelativeUrl = url.indexOf(':') === -1;
/* var isSameDomain = url.indexOf(document.domain) > -1;*/
// if ( /*isSameDomain || */ isRelativeUrl) {
if (!isRelativeUrl && startsWithRootUrl(self, url)) {
url = getRelativeUrl(url);
}
if (shouldHandleNavigation(self, url)) {
e.preventDefault();
self.setUrlDebounced(url);
}
}
// function makeRelativeUrlStartWithSlash(self, url) {
// const isRelativeUrl = url.indexOf(':') === -1;
// let result = url;
// if (isRelativeUrl) {
// // Replace all (/.../g) leading slash (^\/) or (|) trailing slash (\/$) with an empty string.
// result = url.replace(/^\/|\/$/g, '');
// result = '/' + result;
// }
// return result;
// }
var RouterStatePush = function () {
function RouterStatePush(router) {
_classCallCheck(this, RouterStatePush);
var self = this;
// http://stackoverflow.com/questions/8980255/how-do-i-retrieve-if-the-popstate-event-comes-from-back-or-forward-actions-with
self.stateId = 0;
self.router = router;
// TODO: Pas besoin de debounce étant donné que le router annule automatiquement les requêtes précédentes... pas certain du résultat --> à valider
self.setUrlDebounced = /* _.debounce( */function (url) {
self.router.navigate(url);
};
/* , 500, {
'leading': true,
'trailing': true
});*/
// TODO: Pas besoin de debounce étant donné que le router annule automatiquement les requêtes précédentes... pas certain du résultat --> à valider
self.backOrForwardDebounced = /*_.debounce(*/function (state) {
var direction = void 0;
if (state.id < self.stateId) {
self.stateId--;
direction = 'back';
} else {
direction = 'forward';
self.stateId++;
}
return self.backOrForward(state, direction);
};
/* , 500, {
'leading': true,
'trailing': true
});*/
// prevent bug with safari (popstate is fired on page load with safari)
document.addEventListener('DOMContentLoaded', function () /*event*/{
// back and forward button support
window.onpopstate = function (e) {
backAndForwardButtonHandler(self, e);
};
});
// http://www.smashingmagazine.com/2013/11/an-introduction-to-dom-events/
document.addEventListener('click', function (event) {
var clickableElement = getClickableElement(event.target);
if (clickableElement) {
hrefClickHandler(self, event, clickableElement);
}
});
}
_createClass(RouterStatePush, [{
key: 'backOrForward',
value: function backOrForward() /* state */{
// même dans le cas où on fait back, il se peut que, dû au pipeline du router, l'url ne
// soit pas celle du back (a cause de guardRoute par exemple)
// il faut donc faire un replace du state à la fin pour être certain d'avoir la bonne url
return this.router.navigate(this.router.currentUrl(), {
replace: true
});
}
}, {
key: 'pushState',
value: function pushState(options) {
var defaultOptions = {
url: '',
pageTitle: '',
stateObject: {},
replace: false
};
var finalOptions = Object.assign({}, defaultOptions, options);
finalOptions.stateObject.url = finalOptions.url;
finalOptions.stateObject.pageTitle = finalOptions.pageTitle;
if (finalOptions.replace) {
finalOptions.stateObject.id = this.stateId;
window.history.replaceState(finalOptions.stateObject, finalOptions.pageTitle, finalOptions.url);
} else {
finalOptions.stateObject.id = ++this.stateId;
window.history.pushState(finalOptions.stateObject, finalOptions.pageTitle, finalOptions.url);
}
}
}]);
return RouterStatePush;
}();
exports.default = RouterStatePush;
});