//////////////////////////////////////////////////////////////////////////////
//
// spa.ja
// (c) Gareth Bult 2017
// MIT License
//
// Depends on: jQuery
//
// Turn any website into a Single Page Application by including this code.
//
//////////////////////////////////////////////////////////////////////////////
//
// we need a global reference so we can add 3rd parth callbacks
//
var iflex_spa_callbacks = iflex_spa_callbacks || {};
//
// on-ready handler, jQuery 3 style
//
jQuery(function() {
//////////////////////////////////////////////////////////////////////////
//
// Configuration - this section will change and adapt as we build up a
// list of edge-cases.
//
//////////////////////////////////////////////////////////////////////////
var href = window.location.href,
has_protocol = new RegExp('^(http|https):','i'),
is_local = new RegExp('^(http:|https:|)//' + window.location.host,'i'),
is_download = new RegExp('\.(iso|torrent|sig|zip)$'),
wp_admin = new RegExp('(/wp-admin/|/wp-login.php)'),
//////////////////////////////////////////////////////////////////////////
//
// jQuery_monkeypatch - add error trapping to the globalEval call
//
//////////////////////////////////////////////////////////////////////////
jQuery_monkeypatch = function() {
jQuery.globalEval = function(b) {
var a = window;
b && jQuery.trim(b) && (a.execScript || function(b) {
try {
a.eval.call(a, b)
} catch(error) {
console.log(error, b);
}
})(b)
}
},
//////////////////////////////////////////////////////////////////////////
//
// scroll_top - reposition the page on a given #tag
//
//////////////////////////////////////////////////////////////////////////
scroll_top = function(tag) {
if(jQuery('#'+tag)) {
node = jQuery('#'+tag).offset();
jQuery('html, body').animate({scrollTop: node.top + 'px'}, 'fast');
}
},
//////////////////////////////////////////////////////////////////////////
//
// set_position - reposition the page based on the current #tag and link
//
//////////////////////////////////////////////////////////////////////////
set_position = function(tag,htag) {
if(htag&&(jQuery('#'+htag).length)) return scroll_top(htag);
if(tag=='href') jQuery('html,body').scrollTop(0);
},
//////////////////////////////////////////////////////////////////////////
//
// move_scripts - move a bunch of scripts to a new target location
//
//////////////////////////////////////////////////////////////////////////
move_scripts = function(scripts, target) {
jQuery.each(scripts, function() {
var script = document.createElement('script'), node = target.lastChild;
if(this.src) {
script.type = this.type;
script.src = this.src;
} else {
script.innerHTML = this.innerHTML;
}
node.parentNode.insertBefore(script, node.nextSibling);
});
},
//////////////////////////////////////////////////////////////////////////
//
// reload_head - add new entries to page head and remove obsoletes
//
//////////////////////////////////////////////////////////////////////////
reload_head = function(head) {
var live = jQuery('head').find('*');
jQuery.each(jQuery(head).find('*'), function() {
var raw = jQuery(this).get()[0], i=live.length;
while(--i >= 0) {
if(raw.isEqualNode(jQuery(live[i]).get()[0])) {
live.splice(i,1);
break;
}
}
if(i<0) jQuery('head').append(this);
});
jQuery.each(jQuery('head').find('*'), function() {
var raw = jQuery(this).get()[0], old = this;
jQuery.each(live, function(k,v) {
if(raw.isEqualNode(jQuery(v).get()[0])) jQuery(old).remove();
});
});
},
//////////////////////////////////////////////////////////////////////////
//
// load_page - bulk of the work, replace the current page with a new one
//
//////////////////////////////////////////////////////////////////////////
load_page = function(tag, href, data, navigate) {
var target, base, url, htag,
replace_page = function(data,status,xhr) {
var mtype,stype,content_type = xhr.getResponseHeader('Content-Type').split(";")[0];
[mtype,stype] = content_type.split("/");
switch(mtype) {
case 'text':
var url, htag, head, body, hscripts, bscripts,
node = document.createElement("html");
window.onload = null; // make sure this is clear for things like SMF
node.innerHTML = data;
head = node.getElementsByTagName("head")[0];
document.title = jQuery(head).find('title').text();
body = node.getElementsByTagName("body")[0];
hscripts = jQuery(head).find('script').remove().get();
bscripts = jQuery(body).find('script').remove().get();
reload_head(head);
move_scripts(hscripts, body);
move_scripts(bscripts, body);
jQuery_monkeypatch();
jQuery("body").html(body.innerHTML);
[url,htag] = href.split('#');
set_position(tag,htag);
setTimeout(function(){jQuery(window).trigger('load');},100);
break;
case 'application':
case 'image':
var win = window.open(href, '_blank');
win.focus();
return false;
default:
alert('Not configured handle file type: ', content_type);
return false;
}
if(navigate) history.pushState({href: href}, null, href);
jQuery.each(iflex_spa_callbacks, function(name,routine){
routine();
});
};
switch(tag) {
case 'action':
target = href;
href = window.location.href;
jQuery.ajax({
url: target,
data: data,
type: 'POST',
success: replace_page,
processData: false,
contentType: false,
cache: false
});
break;
case 'href':
base = window.location.href.split('#')[0];
[url,htag] = href.split('#');
if(htag&&(!url||url==base)) return scroll_top(htag);
if(!htag||(url&&(url!=base))) jQuery.get(href, replace_page).fail(function(){
alert('Unable to locate url '+href);
});
}
},
//////////////////////////////////////////////////////////////////////////
//
// outside - work out if a given URL is going to cause us to leave site
//
//////////////////////////////////////////////////////////////////////////
outside = function(href) {
if(has_protocol.test(href) && !is_local.test(href)) return true;
if(is_download.test(href)||wp_admin.test(href)) return true;
return false;
},
//////////////////////////////////////////////////////////////////////////
//
// wrap - assign a new handler to an element (if appropriate)
//
//////////////////////////////////////////////////////////////////////////
wrap = function(self, tag, handler) {
//
// if we have no url, or the url is outside the site ...
//
var href = jQuery(self).attr(tag);
if(!href||(href=='#')||outside(href)) return false;
//
// add a handler to override the default process
//
jQuery(self).on(handler, function(event){
var not_supported = false,
data = null,
button = null;
//
// make sure we've not been beaten to the punch
//
if(event.isDefaultPrevented()) return false;
switch(tag) {
case 'action':
//
// for POST requests we're going to use the results from
// FormData, and add the name/value of the submit button
//
data = new FormData(this);
button = document.activeElement;
if(button.name) data.append(button.name, button.value)
break;
case 'href':
break;
default:
not_supported = true;
break;
}
if(not_supported) return;
event.preventDefault();
load_page(tag,href,data,true);
});
},
//////////////////////////////////////////////////////////////////////////
//
// back_butto - retask the back button to do a soft back if within site
//
//////////////////////////////////////////////////////////////////////////
back_button = function() {
//
// If we're run out of history, exit the site
//
if(!history.state) return window.history.back();
//
// Otherwise, implement our own version of bac
//
load_page('href', history.state.href, null, false);
};
//////////////////////////////////////////////////////////////////////////
//
// ~~~ MAIN ~~~
//
//////////////////////////////////////////////////////////////////////////
//
// wrap all A links for FORMs
//
jQuery('a').each(function(){wrap(this,'href','click')});
jQuery('form').each(function(){wrap(this,'action','submit')});
//
// wrap the browser's 'BACK' button
//
jQuery(window).off('popstate').on('popstate', back_button);
if(history.state) return;
//
// record our first page of the session
//
history.pushState({href:href},null,href);
});Travelled to 3 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx
No comments. add comment
| Snippet ID: | #1031909 |
| Snippet name: | iflex_spa.js - turn website into single page app |
| Eternal ID of this version: | #1031909/1 |
| Text MD5: | 86b634ecae08ea3f51d33b22f7807428 |
| Author: | stefan |
| Category: | javascript |
| Type: | Document |
| Public (visible to everyone): | Yes |
| Archived (hidden from active list): | No |
| Created/modified: | 2021-07-25 05:57:19 |
| Source code size: | 10789 bytes / 272 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 352 / 111 |
| Referenced in: | -