mpa-enhancer
Advanced tools
Comparing version 1.1.0 to 2.0.0
@@ -40,7 +40,3 @@ // @ts-check | ||
let { y, height, href, active: { id, name } } = JSON.parse(location) | ||
if (!hasAttr(doc.body, 'mpa-skip-scroll') | ||
&& href === getCleanUrlPath() | ||
&& y) { | ||
w.scrollTo({ top: y + doc.body.scrollHeight - height }) | ||
} | ||
let active = | ||
@@ -52,2 +48,8 @@ doc.getElementById(id) | ||
run('select', active) | ||
if (!hasAttr(doc.body, 'mpa-skip-scroll') | ||
&& href === getCleanUrlPath() | ||
&& y) { | ||
w.scrollTo({ top: y + doc.body.scrollHeight - height }) | ||
} | ||
} | ||
@@ -54,0 +56,0 @@ |
@@ -40,26 +40,65 @@ // node_modules/idb-keyval/dist/index.js | ||
// node_modules/html-es6cape/dist/index.esm.js | ||
var t = { "&": "&", ">": ">", "<": "<", '"': """, "'": "'", "`": "`" }; | ||
var e = new RegExp(Object.keys(t).join("|"), "g"); | ||
function n(n2) { | ||
return void 0 === n2 && (n2 = ""), String(n2).replace(e, function(e3) { | ||
return t[e3]; | ||
}); | ||
// node_modules/html-template-tag-stream/lib/index.js | ||
var chars = { | ||
"&": "&", | ||
">": ">", | ||
"<": "<", | ||
'"': """, | ||
"'": "'", | ||
"`": "`" | ||
}; | ||
var chars_default = chars; | ||
var re = new RegExp(Object.keys(chars_default).join("|"), "g"); | ||
function escape(str = "") { | ||
return String(str).replace(re, (match) => chars_default[match]); | ||
} | ||
// node_modules/html-template-tag/dist/index.esm.js | ||
function e2(e3) { | ||
for (var a = [], t2 = 1; t2 < arguments.length; t2++) | ||
a[t2 - 1] = arguments[t2]; | ||
return e3.raw.reduce(function(t3, n2, i) { | ||
var o = a[i - 1]; | ||
return Array.isArray(o) ? o = o.join("") : e3.raw[i - 1] && e3.raw[i - 1].endsWith("$") ? t3 = t3.slice(0, -1) : o = n(o), t3 + o + n2; | ||
}); | ||
var html_es6cape_default = escape; | ||
var htmlPrototype = Object.getPrototypeOf(html); | ||
async function* typeChecker(sub, isRawHtml) { | ||
const type = typeof sub, isPromise = sub instanceof Promise; | ||
if (sub == null) { | ||
} else if (type === "string") { | ||
yield isRawHtml ? sub : html_es6cape_default(sub); | ||
} else if (type === "number") { | ||
yield "" + sub; | ||
} else if (isPromise || sub instanceof Function) { | ||
sub = isPromise ? await sub : sub(); | ||
for await (const s of typeChecker(sub, isRawHtml)) { | ||
yield s; | ||
} | ||
} else if (Array.isArray(sub)) { | ||
for await (const s of sub) { | ||
for await (const x of typeChecker(s, true)) { | ||
yield x; | ||
} | ||
} | ||
} else if (sub.constructor === htmlPrototype) { | ||
for await (const s of sub) { | ||
yield s; | ||
} | ||
} else { | ||
yield isRawHtml ? sub.toString() : html_es6cape_default(sub.toString()); | ||
} | ||
} | ||
async function* html(literals, ...subs) { | ||
const lits = literals.raw, length = lits.length; | ||
let isRawHtml = true; | ||
for (let i = 0; i < length; i++) { | ||
let lit = lits[i]; | ||
const sub = subs[i - 1]; | ||
for await (const s of typeChecker(sub, isRawHtml)) { | ||
yield s; | ||
} | ||
lit = (isRawHtml = lit.endsWith("$")) ? lit.slice(0, -1) : lit; | ||
if (lit) | ||
yield lit; | ||
} | ||
} | ||
var async_generator_html_default = html; | ||
// src-todo/server/layout.ts | ||
function todoView({ completed, title, id }, enableJS) { | ||
function todoView({ completed, title, id }) { | ||
let completedClass = completed ? "completed" : ""; | ||
let liClass = `class="${completedClass}"`; | ||
return e2` | ||
return async_generator_html_default` | ||
<li $${liClass}> | ||
@@ -89,3 +128,3 @@ <form method="post" action="?handler=toggle-complete&id=$${"" + id}"> | ||
function layout(todos, activeCount, count, enableJS) { | ||
return e2` | ||
return async_generator_html_default` | ||
<!doctype html> | ||
@@ -121,3 +160,3 @@ <html lang="en"> | ||
</form> | ||
<ul id="todo-list" class="todo-list">$${todos.map((x) => todoView(x, enableJS)).join("")}</ul> | ||
<ul id="todo-list" class="todo-list">${todos.map((x) => todoView(x, enableJS))}</ul> | ||
</section> | ||
@@ -137,3 +176,3 @@ <!-- This footer should be hidden by default and shown when there are todos --> | ||
<!--Hidden if no completed items are left ↓ --> | ||
$${count - activeCount === 0 ? "" : e2`<form method="post" action="?handler=clear-completed"> | ||
$${count - activeCount === 0 ? "" : async_generator_html_default`<form method="post" action="?handler=clear-completed"> | ||
<button id="clear-completed" class="clear-completed">Clear completed</button> | ||
@@ -244,4 +283,4 @@ </form>`} | ||
var root = self.location.pathname.replace("/sw.js", ""); | ||
self.addEventListener("install", async (e3) => { | ||
e3.waitUntil( | ||
self.addEventListener("install", async (e) => { | ||
e.waitUntil( | ||
caches.open(version).then((cache) => cache.addAll([ | ||
@@ -256,34 +295,46 @@ "/js/sw-loader.js", | ||
}); | ||
self.addEventListener("fetch", (e3) => e3.respondWith(getResponse(e3))); | ||
self.addEventListener("activate", async (e3) => { | ||
self.addEventListener("fetch", (e) => e.respondWith(getResponse(e))); | ||
self.addEventListener("activate", async (e) => { | ||
console.log(`Service worker activated. Cache version '${version}'.`); | ||
const keys = await caches.keys(); | ||
if (e3.waitUntil) { | ||
if (e.waitUntil) { | ||
let cacheDeletes = keys.map((x) => version !== x && caches.delete(x)).filter((x) => x); | ||
if (cacheDeletes.length === 0) | ||
return; | ||
e3.waitUntil(Promise.all(cacheDeletes)); | ||
e.waitUntil(Promise.all(cacheDeletes)); | ||
} | ||
}); | ||
async function getResponse(e3) { | ||
const url = new URL(e3.request.url); | ||
async function getResponse(e) { | ||
const url = new URL(e.request.url); | ||
console.log(`Fetching '${url.pathname}'`); | ||
if (url.pathname === root + "/" && e3.request.method === "GET") { | ||
const index = await getAll({ request: e3.request, url }); | ||
return new Response(index, { | ||
headers: { | ||
"Content-Type": "text/html" | ||
} | ||
}); | ||
if (url.pathname === root + "/" && e.request.method === "GET") { | ||
const index = await getAll({ request: e.request, url }); | ||
return streamResponse(index); | ||
} | ||
const handler = url.searchParams.get("handler"); | ||
if (handler) { | ||
return handle(handler, e3.request, url); | ||
return handle(handler, e.request, url); | ||
} | ||
return caches.match(url.pathname); | ||
} | ||
var encoder = new TextEncoder(); | ||
function streamResponse(generator) { | ||
const stream = new ReadableStream({ | ||
async start(controller) { | ||
for await (let s of generator) { | ||
controller.enqueue(encoder.encode(s)); | ||
} | ||
controller.close(); | ||
} | ||
}); | ||
return new Response( | ||
stream, | ||
{ | ||
headers: { "content-type": "text/html; charset=utf-8" } | ||
} | ||
); | ||
} | ||
async function handle(handler, request, url) { | ||
const data = await getData(request, url); | ||
const opt = { request, url, data }; | ||
let task = null; | ||
switch (handler) { | ||
@@ -290,0 +341,0 @@ case "create": |
{ | ||
"name": "mpa-enhancer", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"description": "Minimalist JavaScript to make your MPA work that much better", | ||
@@ -22,3 +22,3 @@ "main": "src/mpa.js", | ||
"dependencies": { | ||
"html-template-tag": "^4.0.1", | ||
"html-template-tag-stream": "^1.1.2", | ||
"idb-keyval": "^6.2.1", | ||
@@ -25,0 +25,0 @@ "todomvc-app-css": "^2.4.2", |
@@ -12,1 +12,9 @@ # MPA Enhancer | ||
## Change Log | ||
**2.0.0**: | ||
Changed the order from scrolling first to focus on what was focused before | ||
first. | ||
@@ -17,3 +17,3 @@ // @ts-ignore | ||
type Handler = (o: RequestOptions) => Promise<void> | ||
type GetHandler = (o: GetHandlerOptions) => Promise<string> | ||
type GetHandler = (o: GetHandlerOptions) => Promise<AsyncGenerator<string>> | ||
@@ -20,0 +20,0 @@ interface Settings { |
@@ -1,6 +0,5 @@ | ||
import html from "html-template-tag" | ||
import html from "html-template-tag-stream" | ||
import { TodoView } from "./actions" | ||
function todoView({ completed, title, id }: TodoView, enableJS: boolean) { | ||
function todoView({ completed, title, id }: TodoView) { | ||
let completedClass = completed ? "completed" : "" | ||
@@ -67,3 +66,3 @@ let liClass = `class="${completedClass}"` | ||
</form> | ||
<ul id="todo-list" class="todo-list">$${todos.map(x => todoView(x, enableJS)).join('')}</ul> | ||
<ul id="todo-list" class="todo-list">${todos.map(x => todoView(x, enableJS))}</ul> | ||
</section> | ||
@@ -70,0 +69,0 @@ <!-- This footer should be hidden by default and shown when there are todos --> |
@@ -52,6 +52,3 @@ import { | ||
const index = await getAll({ request: e.request, url }) | ||
return new Response(index, { | ||
headers: { | ||
"Content-Type": "text/html", | ||
} }) | ||
return streamResponse(index) | ||
} | ||
@@ -67,6 +64,22 @@ | ||
const encoder = new TextEncoder() | ||
function streamResponse(generator: AsyncGenerator<any, void, unknown>) { | ||
const stream = new ReadableStream({ | ||
async start(controller : ReadableStreamDefaultController<any>) { | ||
for await (let s of generator) { | ||
controller.enqueue(encoder.encode(s)) | ||
} | ||
controller.close() | ||
} | ||
}) | ||
return new Response( | ||
stream, { | ||
headers: { "content-type": "text/html; charset=utf-8" } | ||
}) | ||
} | ||
async function handle(handler: string, request: Request, url: URL) { | ||
const data = await getData(request, url) | ||
const opt = { request, url, data } | ||
let task = null | ||
switch (handler) { | ||
@@ -73,0 +86,0 @@ case "create": |
@@ -40,7 +40,3 @@ // @ts-check | ||
let { y, height, href, active: { id, name } } = JSON.parse(location) | ||
if (!hasAttr(doc.body, 'mpa-skip-scroll') | ||
&& href === getCleanUrlPath() | ||
&& y) { | ||
w.scrollTo({ top: y + doc.body.scrollHeight - height }) | ||
} | ||
let active = | ||
@@ -52,2 +48,8 @@ doc.getElementById(id) | ||
run('select', active) | ||
if (!hasAttr(doc.body, 'mpa-skip-scroll') | ||
&& href === getCleanUrlPath() | ||
&& y) { | ||
w.scrollTo({ top: y + doc.body.scrollHeight - height }) | ||
} | ||
} | ||
@@ -54,0 +56,0 @@ |
@@ -1,1 +0,1 @@ | ||
(()=>{let t=document,n=window,a=t.querySelector.bind(t);function i(e,o){return e.hasAttribute(o)}function l(){return new URL(t.location.href).pathname.replace(/\/$/,"")}n.addEventListener("beforeunload",()=>{let e=t.activeElement;localStorage.pageLocation=JSON.stringify({href:l(),y:n.scrollY,height:t.body.scrollHeight,active:{id:e?.id,name:e?.getAttribute("name")}})});function u(){if(a("[autofocus]"))return;let e=localStorage.pageLocation;if(!e)return;let{y:o,height:f,href:s,active:{id:d,name:g}}=JSON.parse(e);!i(t.body,"mpa-skip-scroll")&&s===l()&&o&&n.scrollTo({top:o+t.body.scrollHeight-f});let r=t.getElementById(d)||a(`[name="${g}"]`);!r||i(r,"mpa-skip-focus")||(c("focus",r),c("select",r))}function c(e,o){o[e]&&o[e]()}u()})(); | ||
(()=>{let t=document,n=window,a=t.querySelector.bind(t);function i(e,o){return e.hasAttribute(o)}function l(){return new URL(t.location.href).pathname.replace(/\/$/,"")}n.addEventListener("beforeunload",()=>{let e=t.activeElement;localStorage.pageLocation=JSON.stringify({href:l(),y:n.scrollY,height:t.body.scrollHeight,active:{id:e?.id,name:e?.getAttribute("name")}})});function u(){if(a("[autofocus]"))return;let e=localStorage.pageLocation;if(!e)return;let{y:o,height:f,href:s,active:{id:d,name:g}}=JSON.parse(e),r=t.getElementById(d)||a(`[name="${g}"]`);!r||i(r,"mpa-skip-focus")||(c("focus",r),c("select",r),!i(t.body,"mpa-skip-scroll")&&s===l()&&o&&n.scrollTo({top:o+t.body.scrollHeight-f}))}function c(e,o){o[e]&&o[e]()}u()})(); |
42520
1396
20
+ Addedhtml-template-tag-stream@1.1.2(transitive)
- Removedhtml-template-tag@^4.0.1
- Removedhtml-element-attributes@3.4.0(transitive)
- Removedhtml-es6cape@2.0.2(transitive)
- Removedhtml-template-tag@4.1.1(transitive)