premonition 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.min.js +1 -2
- package/package.json +28 -30
- package/readme.md +98 -82
- package/.babelrc +0 -11
- package/demo/_post.hbs +0 -30
- package/demo/_readnext.hbs +0 -3
- package/demo/entry-a/index.html +0 -35
- package/demo/entry-a/readme.md +0 -12
- package/demo/entry-b/index.html +0 -35
- package/demo/entry-b/readme.md +0 -12
- package/demo/entry-c/index.html +0 -35
- package/demo/entry-c/readme.md +0 -12
- package/demo/entry-d/index.html +0 -35
- package/demo/entry-d/readme.md +0 -12
- package/demo/entry-e/index.html +0 -35
- package/demo/entry-e/readme.md +0 -12
- package/demo/entry-f/index.html +0 -35
- package/demo/entry-f/readme.md +0 -12
- package/demo/index.hbs +0 -54
- package/demo/index.html +0 -96
- package/index.html +0 -116
- package/rollup.config.js +0 -35
- package/src/fquery.js +0 -1
- package/src/head.js +0 -38
- package/src/index.js +0 -53
- package/src/init.js +0 -30
- package/src/inject.js +0 -29
- package/src/links.js +0 -15
- package/src/load.js +0 -26
- package/src/open.js +0 -26
- package/src/replace.js +0 -35
- package/src/validate.js +0 -17
- package/web/_home.hbs +0 -29
- package/web/_nav.hbs +0 -1
- package/web/style.min.css +0 -1
- package/web/style.scss +0 -64
package/demo/entry-e/readme.md
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
layout: post.hbs
|
|
3
|
-
title: Entry E
|
|
4
|
-
date: 2018-06-19 20:00
|
|
5
|
-
categories: jekyll mini clone
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Entry E
|
|
9
|
-
|
|
10
|
-
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
|
11
|
-
|
|
12
|
-
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
package/demo/entry-f/index.html
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
7
|
-
<title>Entry F</title>
|
|
8
|
-
<link rel="stylesheet" href="/web/style.min.css">
|
|
9
|
-
</head>
|
|
10
|
-
<body>
|
|
11
|
-
<nav>
|
|
12
|
-
<a class="brand" href="/">premonition.js</a>
|
|
13
|
-
<div class="menu">
|
|
14
|
-
<a href="/demo">Demo</a>
|
|
15
|
-
<a href="https://github.com/franciscop/premonition">Github</a>
|
|
16
|
-
</div>
|
|
17
|
-
</nav>
|
|
18
|
-
|
|
19
|
-
<article>
|
|
20
|
-
<a href="/demo/">All Articles</a>
|
|
21
|
-
<h1 id="entry-f">Entry F</h1>
|
|
22
|
-
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
|
|
23
|
-
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
|
24
|
-
|
|
25
|
-
<h2>Read Next</h2>
|
|
26
|
-
|
|
27
|
-
Example of a partial to see how they work
|
|
28
|
-
</article>
|
|
29
|
-
Hello
|
|
30
|
-
<script src="/index.min.js" data-pre-once></script>
|
|
31
|
-
<script type="text/javascript">
|
|
32
|
-
console.log('NO PARSING NEEDED');
|
|
33
|
-
</script>
|
|
34
|
-
</body>
|
|
35
|
-
</html>
|
package/demo/entry-f/readme.md
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
layout: post.hbs
|
|
3
|
-
title: Entry F
|
|
4
|
-
date: 2018-06-19 20:00
|
|
5
|
-
categories: jekyll mini clone
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Entry F
|
|
9
|
-
|
|
10
|
-
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
|
11
|
-
|
|
12
|
-
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
package/demo/index.hbs
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
7
|
-
<title>premonition.js demo</title>
|
|
8
|
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/themes/prism-okaidia.min.css">
|
|
9
|
-
<link rel="stylesheet" href="/web/style.min.css">
|
|
10
|
-
</head>
|
|
11
|
-
<body>
|
|
12
|
-
<nav>
|
|
13
|
-
<a class="brand" href="/">premonition.js</a>
|
|
14
|
-
<div class="menu">
|
|
15
|
-
<a href="/demo">Demo</a>
|
|
16
|
-
<a href="https://github.com/franciscop/premonition">Github</a>
|
|
17
|
-
</div>
|
|
18
|
-
</nav>
|
|
19
|
-
|
|
20
|
-
<main>
|
|
21
|
-
<h1>Premonition demo</h1>
|
|
22
|
-
<p>Click around the different links. We are rendering a "cached!" tag when an element is cached, and removing it when the cache has expired.</p>
|
|
23
|
-
<div class="flex two">
|
|
24
|
-
{{#each blog}}
|
|
25
|
-
{{#if this.date}}
|
|
26
|
-
<div>
|
|
27
|
-
<a href="/demo/{{this.id}}/">
|
|
28
|
-
<h2 id="{{this.id}}">{{this.title}}</h2>
|
|
29
|
-
<time datetime="{{this.date}}">{{this.date}}</time>
|
|
30
|
-
</a>
|
|
31
|
-
</div>
|
|
32
|
-
{{/if}}
|
|
33
|
-
{{/each}}
|
|
34
|
-
</div>
|
|
35
|
-
<div>
|
|
36
|
-
<h3>Same-page links</h3>
|
|
37
|
-
{{#each blog}}
|
|
38
|
-
<div>
|
|
39
|
-
<a href="#{{this.id}}">{{this.title}}</a>
|
|
40
|
-
</div>
|
|
41
|
-
{{/each}}
|
|
42
|
-
</div>
|
|
43
|
-
</main>
|
|
44
|
-
|
|
45
|
-
<script src="https://unpkg.com/moment@2.22.2/min/moment.min.js" data-pre-once></script>
|
|
46
|
-
<script src="/index.min.js" data-once></script>
|
|
47
|
-
<script type="text/javascript">
|
|
48
|
-
console.log('PARSING DATES');
|
|
49
|
-
document.querySelectorAll('[datetime]').forEach(el => {
|
|
50
|
-
el.innerHTML = moment(el.getAttribute('datetime')).fromNow();
|
|
51
|
-
});
|
|
52
|
-
</script>
|
|
53
|
-
</body>
|
|
54
|
-
</html>
|
package/demo/index.html
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
7
|
-
<title>premonition.js demo</title>
|
|
8
|
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/themes/prism-okaidia.min.css">
|
|
9
|
-
<link rel="stylesheet" href="/web/style.min.css">
|
|
10
|
-
</head>
|
|
11
|
-
<body>
|
|
12
|
-
<nav>
|
|
13
|
-
<a class="brand" href="/">premonition.js</a>
|
|
14
|
-
<div class="menu">
|
|
15
|
-
<a href="/demo">Demo</a>
|
|
16
|
-
<a href="https://github.com/franciscop/premonition">Github</a>
|
|
17
|
-
</div>
|
|
18
|
-
</nav>
|
|
19
|
-
|
|
20
|
-
<main>
|
|
21
|
-
<h1>Premonition demo</h1>
|
|
22
|
-
<p>Click around the different links. We are rendering a "cached!" tag when an element is cached, and removing it when the cache has expired.</p>
|
|
23
|
-
<div class="flex two">
|
|
24
|
-
<div>
|
|
25
|
-
<a href="/demo/entry-a/">
|
|
26
|
-
<h2 id="entry-a">Entry A</h2>
|
|
27
|
-
<time datetime="2018-06-19 20:00">2018-06-19 20:00</time>
|
|
28
|
-
</a>
|
|
29
|
-
</div>
|
|
30
|
-
<div>
|
|
31
|
-
<a href="/demo/entry-b/">
|
|
32
|
-
<h2 id="entry-b">Entry B</h2>
|
|
33
|
-
<time datetime="2018-06-19 20:00">2018-06-19 20:00</time>
|
|
34
|
-
</a>
|
|
35
|
-
</div>
|
|
36
|
-
<div>
|
|
37
|
-
<a href="/demo/entry-c/">
|
|
38
|
-
<h2 id="entry-c">Entry C</h2>
|
|
39
|
-
<time datetime="2018-06-19 20:00">2018-06-19 20:00</time>
|
|
40
|
-
</a>
|
|
41
|
-
</div>
|
|
42
|
-
<div>
|
|
43
|
-
<a href="/demo/entry-d/">
|
|
44
|
-
<h2 id="entry-d">Entry D</h2>
|
|
45
|
-
<time datetime="2018-06-19 20:00">2018-06-19 20:00</time>
|
|
46
|
-
</a>
|
|
47
|
-
</div>
|
|
48
|
-
<div>
|
|
49
|
-
<a href="/demo/entry-e/">
|
|
50
|
-
<h2 id="entry-e">Entry E</h2>
|
|
51
|
-
<time datetime="2018-06-19 20:00">2018-06-19 20:00</time>
|
|
52
|
-
</a>
|
|
53
|
-
</div>
|
|
54
|
-
<div>
|
|
55
|
-
<a href="/demo/entry-f/">
|
|
56
|
-
<h2 id="entry-f">Entry F</h2>
|
|
57
|
-
<time datetime="2018-06-19 20:00">2018-06-19 20:00</time>
|
|
58
|
-
</a>
|
|
59
|
-
</div>
|
|
60
|
-
</div>
|
|
61
|
-
<div>
|
|
62
|
-
<h3>Same-page links</h3>
|
|
63
|
-
<div>
|
|
64
|
-
<a href="#entry-a">Entry A</a>
|
|
65
|
-
</div>
|
|
66
|
-
<div>
|
|
67
|
-
<a href="#entry-b">Entry B</a>
|
|
68
|
-
</div>
|
|
69
|
-
<div>
|
|
70
|
-
<a href="#entry-c">Entry C</a>
|
|
71
|
-
</div>
|
|
72
|
-
<div>
|
|
73
|
-
<a href="#entry-d">Entry D</a>
|
|
74
|
-
</div>
|
|
75
|
-
<div>
|
|
76
|
-
<a href="#entry-e">Entry E</a>
|
|
77
|
-
</div>
|
|
78
|
-
<div>
|
|
79
|
-
<a href="#entry-f">Entry F</a>
|
|
80
|
-
</div>
|
|
81
|
-
<div>
|
|
82
|
-
<a href="#premonition">premonition.js</a>
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
</main>
|
|
86
|
-
|
|
87
|
-
<script src="https://unpkg.com/moment@2.22.2/min/moment.min.js" data-pre-once></script>
|
|
88
|
-
<script src="/index.min.js" data-once></script>
|
|
89
|
-
<script type="text/javascript">
|
|
90
|
-
console.log('PARSING DATES');
|
|
91
|
-
document.querySelectorAll('[datetime]').forEach(el => {
|
|
92
|
-
el.innerHTML = moment(el.getAttribute('datetime')).fromNow();
|
|
93
|
-
});
|
|
94
|
-
</script>
|
|
95
|
-
</body>
|
|
96
|
-
</html>
|
package/index.html
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
7
|
-
<title>premonition.js</title>
|
|
8
|
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/themes/prism-okaidia.min.css">
|
|
9
|
-
<link rel="stylesheet" href="/web/style.min.css">
|
|
10
|
-
</head>
|
|
11
|
-
<body>
|
|
12
|
-
<nav>
|
|
13
|
-
<a class="brand" href="/">premonition.js</a>
|
|
14
|
-
<div class="menu">
|
|
15
|
-
<a href="/demo">Demo</a>
|
|
16
|
-
<a href="https://github.com/franciscop/premonition">Github</a>
|
|
17
|
-
</div>
|
|
18
|
-
</nav>
|
|
19
|
-
|
|
20
|
-
<article>
|
|
21
|
-
<h1 id="preload-app">Preload App</h1>
|
|
22
|
-
<blockquote>
|
|
23
|
-
<p><strong>Early experiment</strong>, use at your own [high] risk.</p>
|
|
24
|
-
</blockquote>
|
|
25
|
-
<p>Make your website load <em>very</em> fast. Converts your website to a Single Page Application. Or just make it faster, it's up to you. Include the script in your website to see it work:</p>
|
|
26
|
-
<pre><code class="language-html"><script src="https://cdn.jsdelivr.net/npm/premonition" data-pre-once></script></code></pre>
|
|
27
|
-
<p>That's it, your website should be noticeably faster now. You can use the <a href="#api">Javascript API</a> or configure it <a href="#options">with the options</a>. See <a href="#troubleshooting">the troubleshooting guide</a> if you have any issue.</p>
|
|
28
|
-
<blockquote>
|
|
29
|
-
<p><strong>Experiment tip</strong>: open <a href="https://cdn.jsdelivr.net/npm/premonition">the CDN link</a>, copy the JS and paste it into your website console for testing this project in a real env in ~10s. If there's any error you'll have to do this again.</p>
|
|
30
|
-
</blockquote>
|
|
31
|
-
<h2 id="api">API</h2>
|
|
32
|
-
<p>You now have this API available:</p>
|
|
33
|
-
<pre><code class="language-js">// Parse the full current website from scratch
|
|
34
|
-
pre.init(MODE); // Note: it is called onload
|
|
35
|
-
|
|
36
|
-
// Open the passed urls or link element
|
|
37
|
-
pre.open(URL);
|
|
38
|
-
|
|
39
|
-
// [pre]load a link into memory for later opening
|
|
40
|
-
pre.load(URL);
|
|
41
|
-
|
|
42
|
-
// Replace the current page by a new url+html
|
|
43
|
-
pre.replace('/hello', '<html>...</html>');
|
|
44
|
-
|
|
45
|
-
// Cache instance. Using tiny-lru: https://www.npmjs.com/package/tiny-lru
|
|
46
|
-
pre.cache = lru(100, false, 0, 100000);
|
|
47
|
-
pre.cache.get(HREF);
|
|
48
|
-
// cache.set is mostly internal; highly prefer pre.load(HREF)
|
|
49
|
-
pre.cache.set(HREF, { href: HREF, html: HTML });
|
|
50
|
-
pre.cache.expire = 1000; // Maximum time of the page
|
|
51
|
-
pre.cache.clear(); // Remove all items from cache (login/logout)
|
|
52
|
-
|
|
53
|
-
// All events happen just BEFORE the actual name, so they can be prevented
|
|
54
|
-
// NOTE: not available yet
|
|
55
|
-
pre.on('ready', e => {}); // The DOM is ready to be used
|
|
56
|
-
pre.on('init', e => {}); // The script initialization is called
|
|
57
|
-
pre.on('load', e => {}); // A single URL is attempted to be preloaded
|
|
58
|
-
pre.on('preload', e => {}); // Alias for 'load'
|
|
59
|
-
pre.on('open', e => {}); // An HREF is going to be open (load+replace)
|
|
60
|
-
pre.on('replace', e => {}); // The website content is going to be updated
|
|
61
|
-
pre.on('fetch', e => {}); // A HREF is going to be fetched
|
|
62
|
-
pre.on('error', e => {}); // There is an error somewhere
|
|
63
|
-
pre.on('cache.get', e => {}); // When a link is read from cache
|
|
64
|
-
pre.on('cache.set', e => {}); // Update the cache with more HREF+HTML
|
|
65
|
-
pre.on('cache.remove', e => {}); // Some HTML is removed from the cache</code></pre>
|
|
66
|
-
<h2 id="options">Options</h2>
|
|
67
|
-
<p>There are global options and per-element options:</p>
|
|
68
|
-
<ul>
|
|
69
|
-
<li>Global options can be specified on the <code><body></code> element or when calling <code>pre.init({ OPTIONS })</code>.</li>
|
|
70
|
-
<li>Element options should be specified in the element (e.g., <code><script></code>) or when calling the specific method.</li>
|
|
71
|
-
</ul>
|
|
72
|
-
<p>When using HTML elements, they are always prefixed by <code>data-pre-NAME=""</code> (except <code>data-pre=""</code>, which is the mode).</p>
|
|
73
|
-
<p>Examples:</p>
|
|
74
|
-
<pre><code class="language-html"><body data-pre="pwa">
|
|
75
|
-
<body data-pre="pwa" data-pre-expire="100s">
|
|
76
|
-
<body data-pre="pwa" data-pre-expire="100s" data-pre-elements="100">
|
|
77
|
-
<body data-pre="preload"></code></pre>
|
|
78
|
-
<h3 id="mode-data-pre-mode">Mode <code>data-pre-mode</code></h3>
|
|
79
|
-
<p>There are several modes that you can tweak within the <code><body></code> tag:</p>
|
|
80
|
-
<pre><code class="language-html"><body data-pre="pwa">...</body>
|
|
81
|
-
<body data-pre="preload">...</body>
|
|
82
|
-
<body data-pre="spa">...</body>
|
|
83
|
-
<body data-pre="off">...</body></code></pre>
|
|
84
|
-
<p>These are useful depending on your type of website and desired performance boost:</p>
|
|
85
|
-
<ul>
|
|
86
|
-
<li><code>pwa</code> (Progressive Web App): the most aggressive one, will load and cache all the internal pages linked from the current page for 24h. Note: still needs a <code>manifest.json</code> to actually become a PWA.</li>
|
|
87
|
-
<li><code>preload</code>: will fetch the link when hovering a link for significantly faster loading. Cache the pages for 100 seconds.</li>
|
|
88
|
-
<li><code>spa</code> (Single Page Application): will fetch the page onmousedown and load the new page in-body. Make the page seem slightly faster. Cache the pages for 10 seconds.</li>
|
|
89
|
-
<li><code>off</code>: will not try to load or cache anything. You can now call the different methods to hook up specific link in specific situations.</li>
|
|
90
|
-
</ul>
|
|
91
|
-
<p><strong>Default mode</strong>: In normal web usage it will use <code>preload</code>, but if it detects it's installed in a device as a PWA it will trigger the <code>pwa</code> mode. If you want tight control specify the mode manually as shown above.</p>
|
|
92
|
-
<blockquote>
|
|
93
|
-
<p>The cache is only in-memory; any page reload or the PWA getting closed will clear it.</p>
|
|
94
|
-
</blockquote>
|
|
95
|
-
<h4 id="picking-a-mode">Picking a mode</h4>
|
|
96
|
-
<p>The mode will determine how aggressively the preload and cache is used and your users data consumption. Until you get used to the details, just don't pick a mode and let premonition.js choose for you.</p>
|
|
97
|
-
<p>Premonition <em>only</em> loads the html and scripts (see <a href="#load-once">skipping scripts</a>). These normally are quite light and ~10-20 average HTML pages are the equivalent of a single image. So, if your site is fairly small, <code>pwa</code> is the recommended way to go.</p>
|
|
98
|
-
<p>If you are in the middle-ground, then <code>preload</code> is nice in that it will boost greatly page load speed while only preloading selectively those links that are hovered.</p>
|
|
99
|
-
<p>If you want a very light experience without any wasted requests for the user but still slightly faster loads, then use the <code>spa</code> mode. It has slightly better performance in mobile (300ms).</p>
|
|
100
|
-
<p>Finally, turn it off if you want to manually and selectively load links in some situations. Use this to load links only when they become visible on scrolling for example.</p>
|
|
101
|
-
<h3 id="load-once">Load Once</h3>
|
|
102
|
-
<p>Evaluate the script only once even if it's available in the new page:</p>
|
|
103
|
-
<pre><code class="language-html"><script src="..." data-pre-once></script></code></pre>
|
|
104
|
-
<p>This <em>does not work with inline scripts</em> since there's no way to identify those otherwise. If you want to ignore a script from one page in another page, you can set <code>data-pre-ignore</code> for anything:</p>
|
|
105
|
-
<pre><code class="language-html"><script src="..." data-pre-ignore></script></code></pre>
|
|
106
|
-
<h2 id="troubleshooting">Troubleshooting</h2>
|
|
107
|
-
<p><strong>Very early experiment</strong>, <a href="https://francisco.io/">contact me</a> if you need any help.</p>
|
|
108
|
-
|
|
109
|
-
</article>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/prism.min.js"></script>
|
|
113
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/components/prism-javascript.min.js"></script>
|
|
114
|
-
<script src="/index.min.js" data-pre-once></script>
|
|
115
|
-
</body>
|
|
116
|
-
</html>
|
package/rollup.config.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// rollup.config.js
|
|
2
|
-
import babel from 'rollup-plugin-babel';
|
|
3
|
-
import resolve from 'rollup-plugin-node-resolve';
|
|
4
|
-
import commonjs from 'rollup-plugin-commonjs';
|
|
5
|
-
import builtinlist from 'builtin-modules';
|
|
6
|
-
import minify from 'rollup-plugin-babel-minify';
|
|
7
|
-
|
|
8
|
-
const pkg = require('./package');
|
|
9
|
-
|
|
10
|
-
export default {
|
|
11
|
-
input: 'src/index.js',
|
|
12
|
-
output: { file: 'index.min.js', format: 'umd', name: 'pre' },
|
|
13
|
-
external: builtinlist,
|
|
14
|
-
plugins: [
|
|
15
|
-
babel({ include: ['node_modules/**'] }),
|
|
16
|
-
resolve({ jsnext: true, main: true, preferBuiltins: false, namedExports: 'pre' }),
|
|
17
|
-
commonjs({
|
|
18
|
-
// non-CommonJS modules will be ignored, but you can also
|
|
19
|
-
// specifically include/exclude files
|
|
20
|
-
include: [ "./index.min.js", "./node_modules/**" ], // Default: undefined
|
|
21
|
-
|
|
22
|
-
// if true then uses of `global` won't be dealt with by this plugin
|
|
23
|
-
ignoreGlobal: false, // Default: false
|
|
24
|
-
|
|
25
|
-
// if false then skip sourceMap generation for CommonJS modules
|
|
26
|
-
sourceMap: false, // Default: true
|
|
27
|
-
}),
|
|
28
|
-
|
|
29
|
-
minify({
|
|
30
|
-
comments: false,
|
|
31
|
-
banner: `// ${pkg.name}@${pkg.version} · MIT · Francisco Presencia https://francisco.io/`,
|
|
32
|
-
bannerNewLine: true
|
|
33
|
-
})
|
|
34
|
-
]
|
|
35
|
-
};
|
package/src/fquery.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default (sel, base = document) => [...base.querySelectorAll(sel)];
|
package/src/head.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const equals = (a, b) => a.outerHTML === b.outerHTML;
|
|
2
|
-
const exists = (stack, el) => !!stack.find(n => equals(n, el));
|
|
3
|
-
|
|
4
|
-
// # Diff the head
|
|
5
|
-
//
|
|
6
|
-
// Replace the current head for the new head. It will happen in 2 passes to avoid
|
|
7
|
-
// any kind of FOUC. There's an edge case where it might not work (see comments).
|
|
8
|
-
//
|
|
9
|
-
// It works in this way:
|
|
10
|
-
// 1. Go through the new head, insert the new elements in the right place
|
|
11
|
-
// 2. Remove any elements that are not in the new page
|
|
12
|
-
//
|
|
13
|
-
// While normally styles are incremental, it might blow up if the combination of
|
|
14
|
-
// the new styles and old styles does not work nicely together
|
|
15
|
-
export default function (head, updated) {
|
|
16
|
-
let index = 0;
|
|
17
|
-
[...updated.children].forEach((node, i) => {
|
|
18
|
-
const up = node.cloneNode(true);
|
|
19
|
-
|
|
20
|
-
const children = [...head.children];
|
|
21
|
-
const partial = children.slice(index);
|
|
22
|
-
if (exists(partial, up)) {
|
|
23
|
-
index += partial.findIndex(n => equals(up, n));
|
|
24
|
-
children[index].setAttribute(this.attrs.keep, true);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
const ref = children[index];
|
|
28
|
-
up.setAttribute(this.attrs.keep, true);
|
|
29
|
-
ref.parentNode.insertBefore(up, ref.nextSibling);
|
|
30
|
-
});
|
|
31
|
-
[...head.children].forEach(node => {
|
|
32
|
-
if (node.getAttribute(this.attrs.keep)) {
|
|
33
|
-
node.removeAttribute(this.attrs.keep);
|
|
34
|
-
} else {
|
|
35
|
-
head.removeChild(node);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
};
|
package/src/index.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
// InstaClick is so broken that I had to do this :(
|
|
2
|
-
|
|
3
|
-
import lru from 'tiny-lru';
|
|
4
|
-
|
|
5
|
-
import init from './init';
|
|
6
|
-
import load from './load';
|
|
7
|
-
import open from './open';
|
|
8
|
-
import links from './links';
|
|
9
|
-
import head from './head';
|
|
10
|
-
import replace from './replace';
|
|
11
|
-
|
|
12
|
-
const Premonition = function ({ attrs } = {}) {
|
|
13
|
-
|
|
14
|
-
// Make sure we are working with the instance even for const a = Premonition()
|
|
15
|
-
if (!(this instanceof Premonition)) return new Premonition({ attrs });
|
|
16
|
-
|
|
17
|
-
// The names of the attributes
|
|
18
|
-
this.attrs = Object.assign({
|
|
19
|
-
once: 'data-pre-once',
|
|
20
|
-
keep: 'data-pre-keep',
|
|
21
|
-
cache: 'data-pre-cached'
|
|
22
|
-
}, attrs);
|
|
23
|
-
|
|
24
|
-
// Loader is the one for the current in-progress request Promises
|
|
25
|
-
this.loader = {};
|
|
26
|
-
|
|
27
|
-
// These are already executed and should not be executed again
|
|
28
|
-
this.loaded = {};
|
|
29
|
-
|
|
30
|
-
// Then they go to the cache. This only stores serializable data.
|
|
31
|
-
// Args: max (items), notify (of changes), ttl, expire (10 seconds)
|
|
32
|
-
this.cache = lru(100, true, 0, 10000);
|
|
33
|
-
|
|
34
|
-
this.cache.onchange = event => this.links();
|
|
35
|
-
|
|
36
|
-
this.init = init;
|
|
37
|
-
this.load = load;
|
|
38
|
-
this.preload = load; // Alias for .load()
|
|
39
|
-
this.open = open;
|
|
40
|
-
this.links = links;
|
|
41
|
-
this.head = head;
|
|
42
|
-
this.replace = replace;
|
|
43
|
-
this.report = error => console.log('Error:', error);
|
|
44
|
-
|
|
45
|
-
window.onpopstate = e => {
|
|
46
|
-
this.open(e.currentTarget.location.href, { history: false });
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Initialize the calls in the current window
|
|
50
|
-
this.init();
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export default (new Premonition());
|
package/src/init.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import $ from './fquery';
|
|
2
|
-
|
|
3
|
-
export default async function (links = 'a') {
|
|
4
|
-
|
|
5
|
-
await new Promise((resolve) => {
|
|
6
|
-
if (document.readyState === "complete") return resolve();
|
|
7
|
-
// without jQuery (doesn't work in older IEs)
|
|
8
|
-
document.addEventListener('DOMContentLoaded', resolve, false);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
// Load the current page
|
|
12
|
-
this.cache.set(window.location.href, {
|
|
13
|
-
href: window.location.href,
|
|
14
|
-
html: $('html')[0].outerHTML
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
$(`script[${this.attrs.once}]`).forEach(scr => this.loaded[scr.src] = true);
|
|
18
|
-
|
|
19
|
-
$(links).forEach(link => {
|
|
20
|
-
// Ignore the totally external links since they cannot be loaded anyway
|
|
21
|
-
if (link.host !== location.host) return;
|
|
22
|
-
|
|
23
|
-
link.addEventListener('mouseover', e => this.load(e.currentTarget));
|
|
24
|
-
link.addEventListener('touchstart', e => this.load(e.currentTarget));
|
|
25
|
-
link.addEventListener('click', e => {
|
|
26
|
-
e.preventDefault();
|
|
27
|
-
return this.open(e.currentTarget);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
};
|
package/src/inject.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
// Inject the old, unevaluated script in the new document
|
|
3
|
-
export default (scr, body, loaded) => new Promise((resolve, reject) => {
|
|
4
|
-
|
|
5
|
-
// If the script src="" was already loaded, no need to load it again
|
|
6
|
-
if (loaded[scr.src]) return resolve();
|
|
7
|
-
|
|
8
|
-
// Create the main script tag
|
|
9
|
-
const script = document.createElement("script");
|
|
10
|
-
|
|
11
|
-
// External source script
|
|
12
|
-
// https://www.danielcrabtree.com/blog/25/gotchas-with-dynamically-adding-script-tags-to-html
|
|
13
|
-
if (scr.src) {
|
|
14
|
-
script.addEventListener('load', resolve);
|
|
15
|
-
script.addEventListener('error', reject);
|
|
16
|
-
// Cannot wrap this unfortunately ¯\_(ツ)_/¯
|
|
17
|
-
script.src = scr.src;
|
|
18
|
-
body.appendChild(script);
|
|
19
|
-
// resolve() goes only inside addEventListener('load', resolve), not here
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Script just has a string, which will be evaluated synchronously
|
|
24
|
-
// Wrap it in a new context to avoid globals being re-assigned
|
|
25
|
-
const wrapped = `(() => {${scr.innerText}})();`;
|
|
26
|
-
script.appendChild(document.createTextNode(wrapped));
|
|
27
|
-
body.appendChild(script);
|
|
28
|
-
resolve();
|
|
29
|
-
});
|
package/src/links.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import $ from './fquery';
|
|
2
|
-
|
|
3
|
-
// Update the links with the corresponding classes
|
|
4
|
-
export default function (sel = 'a') {
|
|
5
|
-
const links = Object.keys(this.cache.cache);
|
|
6
|
-
$(sel).forEach(link => {
|
|
7
|
-
if (links.includes(link.href)) {
|
|
8
|
-
if (link.getAttribute(this.attrs.cache)) return;
|
|
9
|
-
return link.setAttribute(this.attrs.cache, true);
|
|
10
|
-
} else {
|
|
11
|
-
if (!link.getAttribute(this.attrs.cache)) return;
|
|
12
|
-
link.removeAttribute(this.attrs.cache);
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
};
|
package/src/load.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import validate from './validate';
|
|
2
|
-
|
|
3
|
-
// Fetch either a URL or an <a> element corresponding HTML
|
|
4
|
-
// TODO: script preloading (https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content#Scripting_and_preloads)
|
|
5
|
-
export default async function (ref = '') {
|
|
6
|
-
let href = validate(ref.href || ref, 'pre.load():');
|
|
7
|
-
|
|
8
|
-
// Remove the local url; for pre.load() we do not need it
|
|
9
|
-
href = href.split('#')[0];
|
|
10
|
-
|
|
11
|
-
// Already loading or already in cache; return the current action
|
|
12
|
-
if (this.loader[href]) return this.loader[href];
|
|
13
|
-
if (this.cache.get(href)) return Promise.resolve(this.cache.get(href));
|
|
14
|
-
|
|
15
|
-
// Add to cache
|
|
16
|
-
this.loader[href] = fetch(href).then(res => {
|
|
17
|
-
if (!res.ok) this.report(new Error(res.statusText));
|
|
18
|
-
return res.text();
|
|
19
|
-
}).then(html => {
|
|
20
|
-
this.cache.set(href, { href, html });
|
|
21
|
-
delete this.loader[href];
|
|
22
|
-
return this.cache.get(href);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
return this.loader[href];
|
|
26
|
-
};
|
package/src/open.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import validate from './validate';
|
|
2
|
-
|
|
3
|
-
// Load either a URL or a <a> element referenced HTML into the body
|
|
4
|
-
export default async function (ref, { history } = {}) {
|
|
5
|
-
try {
|
|
6
|
-
// Accept an <a>, since those will have a property called .href
|
|
7
|
-
const href = validate(ref.href || ref);
|
|
8
|
-
|
|
9
|
-
// Retrieve the hash section of it
|
|
10
|
-
const hash = href.split('#').slice(1).join('#');
|
|
11
|
-
|
|
12
|
-
// TODO: make sure that the url is not the current one with a fragment
|
|
13
|
-
|
|
14
|
-
// .load() already does all the cache and loading checking
|
|
15
|
-
const data = await this.load(href);
|
|
16
|
-
|
|
17
|
-
// Merge the data from the new site with the current one
|
|
18
|
-
await this.replace(data.href, data.html, { history });
|
|
19
|
-
|
|
20
|
-
return this.init();
|
|
21
|
-
} catch (error) {
|
|
22
|
-
// Manually load the new page
|
|
23
|
-
this.report(error);
|
|
24
|
-
location.href = href;
|
|
25
|
-
}
|
|
26
|
-
};
|
package/src/replace.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import $ from './fquery';
|
|
2
|
-
import inject from './inject';
|
|
3
|
-
|
|
4
|
-
// Set the new content to whatever it is passed
|
|
5
|
-
export default function (href, html, { history = true } = {}) {
|
|
6
|
-
// URL & History
|
|
7
|
-
// This has to be before the DOM manipulations, otherwise the browser might
|
|
8
|
-
// think that the data is the old one
|
|
9
|
-
if (history) {
|
|
10
|
-
window.history.pushState({ href }, "", href);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
window.scrollTo(0, 0);
|
|
14
|
-
|
|
15
|
-
// Generate a "virtual dom" (no, not your React virtual dom)
|
|
16
|
-
const dom = document.createElement("html");
|
|
17
|
-
dom.innerHTML = html;
|
|
18
|
-
|
|
19
|
-
// Body replacement
|
|
20
|
-
const body = document.body;
|
|
21
|
-
body.innerHTML = '';
|
|
22
|
-
[...$('body', dom)[0].childNodes].forEach(node => body.appendChild(node));
|
|
23
|
-
|
|
24
|
-
// Head replacement
|
|
25
|
-
this.head(document.head, dom.querySelector('head'));
|
|
26
|
-
|
|
27
|
-
// Recursive iteration over the scripts when they have finished loading
|
|
28
|
-
const flipScript = ([current, ...scripts]) => {
|
|
29
|
-
if (!current) return Promise.resolve();
|
|
30
|
-
return inject(current, body, this.loaded).then(() => flipScript(scripts));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Inject each of the scripts sequentially
|
|
34
|
-
return flipScript($('script'));
|
|
35
|
-
};
|
package/src/validate.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export default (href, pre = '') => {
|
|
2
|
-
// Already cached
|
|
3
|
-
if (!href) {
|
|
4
|
-
throw new Error(`${pre} the argument has to be an url or a <a> element`);
|
|
5
|
-
}
|
|
6
|
-
if (href.nodeName === 'A') {
|
|
7
|
-
throw new Error(`${pre} argument element is missing the href="" or it is empty`);
|
|
8
|
-
}
|
|
9
|
-
if (href.nodeName) {
|
|
10
|
-
throw new Error(`${pre} expected an anchor <a> element, received "${href.nodeName}"`);
|
|
11
|
-
}
|
|
12
|
-
if (typeof href !== 'string') {
|
|
13
|
-
throw new Error(`${pre} expected a string, received ${typeof href}`);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return href;
|
|
17
|
-
};
|