litespeed.js
Version:
Lite & fast micro javascript framework that is easy to learn
139 lines (105 loc) • 5.01 kB
JavaScript
window.ls.container.get('view').add({
selector: 'data-ls-router',
controller: function(element, window, document, view, router) {
let firstFromServer = (element.getAttribute('data-first-from-server') === 'true');
let scope = {
selector: 'data-ls-scope',
template: false,
repeat: true,
controller: function() {},
};
let init = function(route) {
let count = parseInt(element.getAttribute('data-ls-scope-count') || 0);
element.setAttribute('data-ls-scope-count', count + 1);
window.scrollTo(0, 0);
if(window.document.body.scrollTo) {
window.document.body.scrollTo(0, 0);
}
router.reset();
if(null === route) {
return; // no view found
}
scope.template = (undefined !== route.view.template) ? route.view.template : null;
scope.controller = (undefined !== route.view.controller) ? route.view.controller : function() {};
document.dispatchEvent(new CustomEvent('state-change'));
if(firstFromServer && null === router.getPrevious()) { // Disable first view, so server could render it faster
scope.template = '';
document.dispatchEvent(new CustomEvent('state-changed'));
}
else if(count === 1) {
view.render(element, function() {
document.dispatchEvent(new CustomEvent('state-changed'));
});
}
else if(null !== router.getPrevious()) {
// console.info('Clean DOM View');
// let new_element = element.cloneNode(true);
// element.parentNode.replaceChild(new_element, element);
// element = new_element;
// console.info('empty element', element);
view.render(element, function() {
document.dispatchEvent(new CustomEvent('state-changed'));
});
}
};
let findParent = function(tagName, el) {
if ((el.nodeName || el.tagName).toLowerCase() === tagName.toLowerCase()){
return el;
}
while (el = el.parentNode){
if ((el.nodeName || el.tagName).toLowerCase() === tagName.toLowerCase()){
return el;
}
}
return null;
};
element.removeAttribute('data-ls-router'); // Avoid re-rendering loop
// Count number of scopes changed to adjust routing logic
element.setAttribute('data-ls-scope', '');
element.setAttribute('data-ls-scope-count', 1);
view.add(scope);
document.addEventListener('click', function(event) { // Handle user navigation
let target = findParent('a', event.target);
if(!target) {
return false; // no a target
}
if(!target.href) { // Just a normal click not an href
return false;
}
if((event.metaKey)) { // CTRL / CMD key is pressed in order to open link in a new tab
return false;
}
if ((target.hasAttribute('target')) && ('_blank' === target.getAttribute('target'))) { // Link should open in a new tab
return false;
}
if(target.hostname !== window.location.hostname) { // Different hostname. Can't do single page magic
return false;
}
let route = router.match(target);
if(null === route) { // No match. this link is not related to our app
return false;
}
event.preventDefault(); // Stop normal browser behavior. Start to act as single page
if(window.location === target.href) { // Same link. Don't re-execute a thing
return false;
}
route.view.state = (undefined === route.view.state) ? true : route.view.state;
if(true === route.view.state) { // Refresh all window on scope change
if(router.getPrevious() && router.getPrevious().view && (router.getPrevious().view.scope !== route.view.scope)) {
window.location.href = target.href;
return false;
}
window.history.pushState({}, 'Unknown', target.href);
}
init(route);
return true;
});
window.addEventListener('popstate', function() { // Handle back button behavior
init(router.match(window.location));
});
window.addEventListener('hashchange', function() { // Handle hash behavior
init(router.match(window.location));
});
init(router.match(window.location)); // Handle first start
}
});