Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

272
LINES

< > BotCompany Repo | #1031909 // iflex_spa.js - turn website into single page app

Document

1  
//////////////////////////////////////////////////////////////////////////////
2  
//
3  
//  spa.ja
4  
//  (c) Gareth Bult 2017
5  
//  MIT License
6  
//
7  
//  Depends on: jQuery
8  
//
9  
//  Turn any website into a Single Page Application by including this code.
10  
//
11  
//////////////////////////////////////////////////////////////////////////////
12  
//
13  
//  we need a global reference so we can add 3rd parth callbacks
14  
//
15  
var iflex_spa_callbacks = iflex_spa_callbacks || {};
16  
//
17  
//  on-ready handler, jQuery 3 style
18  
//
19  
jQuery(function() {
20  
21  
    //////////////////////////////////////////////////////////////////////////
22  
    //
23  
    //  Configuration - this section will change and adapt as we build up a
24  
    //                  list of edge-cases.
25  
    //
26  
    //////////////////////////////////////////////////////////////////////////
27  
    var href = window.location.href,
28  
        has_protocol = new RegExp('^(http|https):','i'),
29  
        is_local = new RegExp('^(http:|https:|)//' + window.location.host,'i'),
30  
        is_download = new RegExp('\.(iso|torrent|sig|zip)$'),
31  
        wp_admin = new RegExp('(/wp-admin/|/wp-login.php)'),
32  
33  
    //////////////////////////////////////////////////////////////////////////
34  
    //
35  
    //  jQuery_monkeypatch - add error trapping to the globalEval call
36  
    //
37  
    //////////////////////////////////////////////////////////////////////////
38  
    jQuery_monkeypatch = function() {
39  
        jQuery.globalEval = function(b) {
40  
            var a = window;
41  
            b && jQuery.trim(b) && (a.execScript || function(b) {
42  
                try {
43  
                    a.eval.call(a, b)
44  
                } catch(error) {
45  
                    console.log(error, b);
46  
                }
47  
            })(b)
48  
        }
49  
    },
50  
51  
    //////////////////////////////////////////////////////////////////////////
52  
    //
53  
    //  scroll_top - reposition the page on a given #tag
54  
    //
55  
    //////////////////////////////////////////////////////////////////////////
56  
    scroll_top = function(tag) {
57  
        if(jQuery('#'+tag)) {
58  
            node = jQuery('#'+tag).offset();
59  
            jQuery('html, body').animate({scrollTop: node.top + 'px'}, 'fast');
60  
        }
61  
    },
62  
63  
    //////////////////////////////////////////////////////////////////////////
64  
    //
65  
    //  set_position - reposition the page based on the current #tag and link
66  
    //
67  
    //////////////////////////////////////////////////////////////////////////
68  
    set_position = function(tag,htag) {
69  
        if(htag&&(jQuery('#'+htag).length)) return scroll_top(htag);
70  
        if(tag=='href') jQuery('html,body').scrollTop(0);
71  
    },
72  
73  
    //////////////////////////////////////////////////////////////////////////
74  
    //
75  
    //  move_scripts - move a bunch of scripts to a new target location
76  
    //
77  
    //////////////////////////////////////////////////////////////////////////
78  
    move_scripts = function(scripts, target) {
79  
        jQuery.each(scripts, function() { 
80  
            var script = document.createElement('script'), node = target.lastChild;
81  
            if(this.src) {
82  
                script.type = this.type;
83  
                script.src = this.src;
84  
            } else {
85  
                script.innerHTML = this.innerHTML;
86  
            }
87  
            node.parentNode.insertBefore(script, node.nextSibling);
88  
        });
89  
    },
90  
91  
    //////////////////////////////////////////////////////////////////////////
92  
    //
93  
    //  reload_head - add new entries to page head and remove obsoletes
94  
    //
95  
    //////////////////////////////////////////////////////////////////////////
96  
    reload_head = function(head) {
97  
        var live = jQuery('head').find('*');
98  
        jQuery.each(jQuery(head).find('*'), function() {
99  
            var raw = jQuery(this).get()[0], i=live.length;
100  
            while(--i >= 0) {
101  
                if(raw.isEqualNode(jQuery(live[i]).get()[0])) {
102  
                    live.splice(i,1);
103  
                    break;
104  
                }
105  
            }
106  
            if(i<0) jQuery('head').append(this);
107  
        });
108  
        jQuery.each(jQuery('head').find('*'), function() {
109  
            var raw = jQuery(this).get()[0], old = this;
110  
            jQuery.each(live, function(k,v) {
111  
                if(raw.isEqualNode(jQuery(v).get()[0])) jQuery(old).remove();
112  
            });
113  
        });
114  
    },
115  
116  
    //////////////////////////////////////////////////////////////////////////
117  
    //
118  
    //  load_page - bulk of the work, replace the current page with a new one
119  
    //
120  
    //////////////////////////////////////////////////////////////////////////
121  
    load_page = function(tag, href, data, navigate) {
122  
        var target, base, url, htag,
123  
        replace_page = function(data,status,xhr) {
124  
            var mtype,stype,content_type = xhr.getResponseHeader('Content-Type').split(";")[0];
125  
            [mtype,stype] = content_type.split("/");
126  
            switch(mtype) {
127  
                case 'text':
128  
                    var url, htag, head, body, hscripts, bscripts,
129  
                    node = document.createElement("html");
130  
                    window.onload = null;   // make sure this is clear for things like SMF
131  
                    node.innerHTML = data;
132  
                    head = node.getElementsByTagName("head")[0];
133  
                    document.title = jQuery(head).find('title').text(); 
134  
                    body = node.getElementsByTagName("body")[0];
135  
                    hscripts = jQuery(head).find('script').remove().get();
136  
                    bscripts = jQuery(body).find('script').remove().get();
137  
                    reload_head(head);
138  
                    move_scripts(hscripts, body);
139  
                    move_scripts(bscripts, body);
140  
                    jQuery_monkeypatch();
141  
                    jQuery("body").html(body.innerHTML);
142  
                    [url,htag] = href.split('#');
143  
                    set_position(tag,htag);
144  
                    setTimeout(function(){jQuery(window).trigger('load');},100);
145  
                    break;
146  
                case 'application':
147  
                case 'image':
148  
                    var win = window.open(href, '_blank');
149  
                    win.focus();
150  
                    return false;
151  
                default:
152  
                    alert('Not configured handle file type: ', content_type);
153  
                    return false;
154  
            }
155  
            if(navigate) history.pushState({href: href}, null, href);
156  
            jQuery.each(iflex_spa_callbacks, function(name,routine){
157  
                routine();
158  
            });
159  
        };
160  
        switch(tag) {
161  
            case 'action':
162  
                target = href;
163  
                href = window.location.href;
164  
                jQuery.ajax({
165  
                    url: target,
166  
                    data: data,
167  
                    type: 'POST',
168  
                    success: replace_page,
169  
                    processData: false,
170  
                    contentType: false,
171  
                    cache: false
172  
                });
173  
                break;
174  
            case 'href':
175  
                base = window.location.href.split('#')[0];
176  
                [url,htag] = href.split('#');
177  
                if(htag&&(!url||url==base)) return scroll_top(htag);
178  
                if(!htag||(url&&(url!=base))) jQuery.get(href, replace_page).fail(function(){
179  
                    alert('Unable to locate url '+href);
180  
                });
181  
        }
182  
    },
183  
184  
    //////////////////////////////////////////////////////////////////////////
185  
    //
186  
    //  outside - work out if a given URL is going to cause us to leave site
187  
    //
188  
    //////////////////////////////////////////////////////////////////////////
189  
    outside = function(href) {
190  
        if(has_protocol.test(href) && !is_local.test(href)) return true;
191  
        if(is_download.test(href)||wp_admin.test(href)) return true;
192  
        return false;
193  
    },
194  
   
195  
    //////////////////////////////////////////////////////////////////////////
196  
    //
197  
    //  wrap - assign a new handler to an element (if appropriate)
198  
    //
199  
    //////////////////////////////////////////////////////////////////////////
200  
    wrap = function(self, tag, handler) {
201  
        //
202  
        //  if we have no url, or the url is outside the site ...
203  
        //       
204  
        var href = jQuery(self).attr(tag);
205  
        if(!href||(href=='#')||outside(href)) return false;
206  
        //
207  
        //  add a handler to override the default process
208  
        //
209  
        jQuery(self).on(handler, function(event){
210  
            var not_supported = false,
211  
                data = null,
212  
                button = null;
213  
            //
214  
            //  make sure we've not been beaten to the punch
215  
            //
216  
            if(event.isDefaultPrevented()) return false;
217  
            switch(tag) {
218  
                case 'action':
219  
                    //
220  
                    //  for POST requests we're going to use the results from
221  
                    //  FormData, and add the name/value of the submit button
222  
                    //
223  
                    data = new FormData(this);
224  
                    button = document.activeElement;
225  
                    if(button.name) data.append(button.name, button.value)
226  
                    break;
227  
                case 'href':
228  
                    break;
229  
                default:
230  
                    not_supported = true;
231  
                    break;
232  
            }
233  
            if(not_supported) return;
234  
            event.preventDefault();
235  
            load_page(tag,href,data,true);
236  
        });
237  
    },
238  
    //////////////////////////////////////////////////////////////////////////
239  
    //
240  
    //  back_butto - retask the back button to do a soft back if within site
241  
    //
242  
    //////////////////////////////////////////////////////////////////////////
243  
    back_button = function() {
244  
        //
245  
        //  If we're run out of history, exit the site
246  
        //
247  
        if(!history.state) return window.history.back();
248  
        //
249  
        //  Otherwise, implement our own version of bac
250  
        //
251  
        load_page('href', history.state.href, null, false);
252  
    };
253  
    //////////////////////////////////////////////////////////////////////////
254  
    //
255  
    //  ~~~ MAIN ~~~ 
256  
    //
257  
    //////////////////////////////////////////////////////////////////////////
258  
    //
259  
    // wrap all A links for FORMs
260  
    //
261  
    jQuery('a').each(function(){wrap(this,'href','click')});
262  
    jQuery('form').each(function(){wrap(this,'action','submit')});
263  
    //
264  
    // wrap the browser's 'BACK' button
265  
    //
266  
    jQuery(window).off('popstate').on('popstate', back_button);
267  
    if(history.state) return;
268  
    //
269  
    // record our first page of the session
270  
    //
271  
    history.pushState({href:href},null,href);
272  
});

download  show line numbers   

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: 151 / 67
Referenced in: [show references]