Files
mama-box/js/host-nav.js

140 lines
8.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ============================================================
host-nav.js · 小程序里换页开「容器页」(2026-11-09 用户)
------------------------------------------------------------
「在任何一个子页面点小程序『复制链接』,打开应该进到那个子页面,现在都进首页」——
「复制链接」复制的是**小程序当前页面**的路径和参数。首页壳里 location.href 换页,小程序页面一直是首页壳,
复制出来永远是首页。所以在小程序里进子页面 / 应用 / 广场笔记时,navigateTo 通用容器
pages/annex/web_view/index?url=<净地址>(分享卡片落地用的同一个页,容器按打开者自己的身份注入),
小程序当前页就是那一页。
MBHost.open(url, fallback) 小程序里开容器页;不在小程序 → false(调用方照旧 location.href);
navigateTo 失败(页面栈满 10 层等)→ 调 fallback,没给就 location.href = url
MBHost.back(fallback) 本页是 open() 开出来的容器页(地址带 _hv=1)→ navigateBack 回上一页,返回 true;
后退失败(复制链接直接打开、没有上一页)→ 调 fallback
MBHost.home(fallback) 同上条件 → switchTab 回小程序首页(首页壳是 tabBar 页,原来停的位置还在)
⚠️ url 只放净地址:去掉 openid/unionid/__no_identity/return_url 和 hash 里的身份,复制出去不带分享人身份。
⚠️ 加 _hv=1 只为让页面知道「我在容器页里」,返回时用 navigateBack,不用在容器里再开一个首页。
11-09 二修(用户:「复制链接打开笔记,点左上角返回回不到首页」):复制链接 / 分享卡片直接打开的容器页没有上一页,
navigateBack 不动。open() 换页前在本机记一笔 mbhost.open,新容器页加载时对上了 = 「从上一页点进来的」,
本页 sessionStorage 记 mbhost.child;back() 只有 child 才 navigateBack,否则 switchTab 回小程序首页。
MBHost.onLeave:open() 跳走前调一下(首页用来记滚动位置,宿主回前台重载首页后回到原位置)。
============================================================ */
(function (g) {
'use strict';
if (g.MBHost) return;
var VIEW = '/pages/annex/web_view/index';
var at = 0;
function mp() { return g.wx && g.wx.miniProgram; }
function inMp() { return g.__wxjs_environment === 'miniprogram' || /miniProgram/i.test(navigator.userAgent); }
function inView() { return /[?&]_hv=1(&|$)/.test(location.search); }
var OK = 'mbhost.open', CK = 'mbhost.child', POS = 'mmbox.homepos.v1'; /* POS:首页跳走前记的滚动位置(mama-box app.js saveHomePos) */
function key(href) { /* 比对用:去掉容器注入的身份 / 时间戳参数 */
var u = new URL(href, location.href);
['openid', 'unionid', 't', '_v', '__no_identity'].forEach(function (k) { u.searchParams.delete(k); });
u.searchParams.sort && u.searchParams.sort();
return u.pathname + u.search;
}
/* 11-09 产后恢复会话报:接了 maternal-bridge 的应用里 window.localStorage 被换成云端镜像(内存 Map),
标记写进镜像、新容器页读不到 → 误判成「直接打开」。这里只用浏览器真实的 localStorage:
加载时是原生的就抓住;已被换掉就从同源空白 iframe 里拿一份;标记另外再写一份 cookie 兜底。 */
var nat = null;
function isNative(x) { try { return !!x && Object.prototype.toString.call(x) === '[object Storage]'; } catch (e) { return false; } }
try { if (isNative(g.localStorage)) nat = g.localStorage; } catch (e) {}
function LS() {
if (nat) return nat;
try {
var f = document.createElement('iframe');
f.style.display = 'none'; f.setAttribute('aria-hidden', 'true');
(document.body || document.documentElement).appendChild(f);
if (isNative(f.contentWindow.localStorage)) nat = f.contentWindow.localStorage;
} catch (e) {}
return nat;
}
function getMark() {
try { var l = LS(), v = l && l.getItem(OK); if (v) return JSON.parse(v); } catch (e) {}
try { var m = document.cookie.match(/(?:^|;\s*)mbhost_open=([^;]*)/); if (m) return JSON.parse(decodeURIComponent(m[1])); } catch (e) {}
return null;
}
function setMark(v) {
try { var l = LS(); if (l) l.setItem(OK, v); } catch (e) {}
try { document.cookie = 'mbhost_open=' + encodeURIComponent(v) + ';path=/;max-age=30;SameSite=Lax'; } catch (e) {}
}
function clearMark() {
try { var l = LS(); if (l) l.removeItem(OK); } catch (e) {}
try { document.cookie = 'mbhost_open=;path=/;max-age=0'; } catch (e) {}
}
function isChild() { try { return sessionStorage.getItem(CK) === '1'; } catch (e) { return false; } }
(function () {
if (!inView()) return;
try {
var m = getMark();
if (m && Date.now() - m.at < 30000 && key(m.u) === key(location.href)) { sessionStorage.setItem(CK, '1'); clearMark(); }
/* 11-09 用户:复制链接 / 分享直接打开的页面,返回首页就停在顶部,不要回到「自己上次点进笔记时」的首页位置 */
if (!isChild()) { var l = LS(); if (l) l.removeItem(POS); }
} catch (e) {}
})();
function toHome(fallback) {
var m = mp();
if (!m || typeof m.switchTab !== 'function') { if (fallback) fallback(); return; }
m.switchTab({ url: '/pages/index/index', fail: function () {
if (typeof m.reLaunch === 'function') m.reLaunch({ url: '/pages/index/index', fail: function () { if (fallback) fallback(); } });
else if (fallback) fallback();
} });
}
function clean(url) {
var u = new URL(url, location.href);
['openid', 'unionid', '__no_identity', 'return_url', 'norestore'].forEach(function (k) { u.searchParams.delete(k); });
if (/(^|[#&])(openid|unionid|phone|nickname|avatar|norestore)=/.test(u.hash)) u.hash = '';
u.searchParams.set('_hv', '1');
return u.href;
}
var H = {
inMp: inMp,
inView: inView,
isChild: isChild,
onLeave: null,
open: function (url, fallback) {
var m = mp();
if (!inMp() || !m || typeof m.navigateTo !== 'function') return false;
if (Date.now() - at < 1000) return true; // 连点:上一次 navigateTo 还在路上
var c;
try { c = clean(url); } catch (e) { return false; }
at = Date.now();
try { if (typeof H.onLeave === 'function') H.onLeave(); } catch (e) {}
setMark(JSON.stringify({ u: c, at: at }));
m.navigateTo({
url: VIEW + '?url=' + encodeURIComponent(c),
fail: function () { at = 0; if (fallback) fallback(); else location.href = url; }
});
return true;
},
back: function (fallback) {
var m = mp();
if (!inView() || !inMp() || !m || typeof m.navigateBack !== 'function') return false;
if (isChild()) m.navigateBack({ delta: 1, fail: function () { toHome(fallback); } });
else toHome(fallback); /* 复制链接 / 分享直接打开:没有上一页 → 回小程序首页 */
return true;
},
home: function (fallback) {
var m = mp();
if (!inView() || !inMp() || !m || typeof m.switchTab !== 'function') return false;
toHome(fallback);
return true;
}
};
g.MBHost = H;
/* 页面里的 <a href> 指向本站别的页面:小程序里也开容器页(别人已经 preventDefault 的不管) */
document.addEventListener('click', function (e) {
if (e.defaultPrevented || e.button || e.metaKey || e.ctrlKey || !inMp()) return;
var a = e.target && e.target.closest && e.target.closest('a[href]');
if (!a || a.target || a.hasAttribute('download')) return;
var href = a.getAttribute('href') || '';
if (!href || href.charAt(0) === '#' || /^(javascript|mailto|tel):/i.test(href)) return;
var u;
try { u = new URL(href, location.href); } catch (err) { return; }
if (u.origin !== location.origin || u.pathname === location.pathname) return;
if (H.open(u.href)) e.preventDefault();
});
})(window);