feat(wxread): align pagination, rendering, and docs

This commit is contained in:
shen
2026-05-24 15:36:01 +08:00
parent a318c0e3d0
commit 2a94921e88
103 changed files with 6124 additions and 4623 deletions
-26
View File
@@ -1,26 +0,0 @@
var generator = {};
generator.generateHtml = function (content, js, css) {
var result = content;
result = result.replace("<!--headTrap<body></body><head></head><html></html>-->", "");
result = result.replace("<!--tailTrap<body></body><head></head><html></html>-->", "");
var match = result.match(/window\.__nonce_str = "(\d+)"/);
var nonce = "";
if (match && match.length > 1) {
nonce = "nonce='" + match[1] + "'";
}
var insertJs = "<script " + (nonce.length > 1 ? nonce : "") + " type='text/javascript'>" + js + "</script></body>";
var insertStyle = "<style type='text/css'>" + css + "</style></head>";
result = result.replace("</head>", insertStyle);
result = result.replace("</body>", insertJs);
return result;
}
generator.generateHtmlForMPReview = function (content,js, css) {
var result = generator.generateHtml(content,js,css)
var modifiedCopyRight = " <span id = 'copyright_logo' stype = 'display:none' > ";
var reg = /<span id="copyright_logo".*>?/;
result = result.replace(reg,modifiedCopyRight);
return result;
}
-26
View File
@@ -1,26 +0,0 @@
(function(){
var head = document.getElementsByTagName("head")[0];
var meta = document.createElement('meta');
meta.name = "viewport";
meta.content = "width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no";
head.appendChild(meta);
var children = document.body.children;
var titleFounded = false;
for(var i = 0; i < children.length; i++){
var child = children[i]
var tagName = child.tagName.toLowerCase()
// 隐藏标题前的空段落
if(!child.textContent){
child.style.display = "none";
}else if(titleFounded){
// 如果找到标题了,那么遇到第一个不为空的短路,就不再处理了
break
}
// 隐藏标题
if(tagName === 'h1' || tagName === 'h2' || tagName === 'h3'){
child.style.display = "none"
titleFounded = true
}
}
})()
-125
View File
@@ -1,125 +0,0 @@
var mediaPlatform = {};
mediaPlatform.getMPHeight = function() {
var height = document.getElementById('js_article').scrollHeight;
return height;
};
mediaPlatform.getMPInfo = function() {
var title;
if (msg_title) {
title = msg_title;
}
else {
title = document.getElementsByTagName('h1')[0].innerHTML.toString();
}
var thumbUrl;
if (msg_cdn_url) {
thumbUrl = msg_cdn_url;
}
else {
thumbUrl = document.getElementsByTagName('img')[0].src.toString();
}
var account;
if (nickname) {
account = nickname;
}
if (!title) {title = "";}
if (!thumbUrl) {thumbUrl= "";}
if (!account) {account = "";}
var info = {
"title": title,
"thumbUrl": thumbUrl,
"account": account,
}
return info;
};
mediaPlatform.getMPInfoStr = function() {
return JSON.stringify(mediaPlatform.getMPInfo());
};
// 这里的做法是为了非独立图片(下方有内容)可以保持跟内容12px的距离
mediaPlatform.handleImageBlank = function() {
var imgs = document.getElementsByTagName('img');
for (var i = 0; i < imgs.length; i++) {
var nextNode = imgs[i].nextSibling;
var preNode = imgs[i].previousSibling;
// 只处理上方有空行的情况,下方有空行经常会跟图片的说明文字连用,尝试修改会导致样式错乱
if (preNode && preNode.nodeName.toLowerCase() == 'br') {
// 处理图片上面有个br,但是br不能自定义高度,所以直接去掉重新加margin
if (preNode.style) {
preNode.style.display = "none";
}
imgs[i].style.marginTop = '12px';
} else if (preNode && preNode.nodeType == 3 && !this.emptyTextNode(preNode)) {
// 处理图片跟上面文字在同一个p里面,但是没有br隔开
imgs[i].style.marginTop = '12px';
}
}
};
mediaPlatform.emptyTextNode = function(el) {
return !el.data || /^(\s|\t)+$/g.test(el.data)
}
// 干掉p>br的情况的margin-top
mediaPlatform.updateBlankHeight = function() {
var blanks = document.getElementsByTagName('br');
for (var i = blanks.length - 1; i >= 0; i--) {
var parentNode = blanks[i].parentNode;
if (parentNode.nodeName.toLowerCase() == "p" && parentNode.childNodes.length == 1) {
parentNode.style.height = '20px';
parentNode.style.minHeight = '0em';
}
}
};
// 观察到部分公众号文章首评的懒加载图片无法正常显示,这是由于 scroll 事件没有触发导致的(怀疑是在 rangy-classapplier 引入后产生了混合反应,但在 Android 上确是好的)。这里的解决办法是手动触发一次 scroll 事件,但由于我们的 webview 其实是完整高度的,本身并不能滚动,所以这样做也不会产生抖动或者其他副作用。
mediaPlatform.protectLazyLoad = function() {
window.scrollTo(0, 1);
};
mediaPlatform.init = function() {
mediaPlatform.updateBlankHeight();
mediaPlatform.handleImageBlank();
mediaPlatform.protectLazyLoad();
};
mediaPlatform.getAuthor = function() {
var find = false;
var author = document.getElementById('js_name');
if (author != null) {
console.log('mediaPlatform.getAuthor ' + author.innerText)
author.addEventListener('click',function(){
wereadBridge.handleWithRichEditor("onClickAuthor",{"param" : author.innerText, "cmd": "onClickAuthor"}, "", "");
}, false)
}
}
mediaPlatform.init();
// 用于在退出公众号文章的时候暂停视频播放
// 由于腾讯视频是通过 iFrame 内嵌的,没有办法直接操作,我们用刷新 iFrame 的方式实现停止播放的效果
function pauseMedia() {
pauseCurrentAudio();
pauseCurrentVideo();
}
function pauseCurrentAudio() {
var audios = document.querySelectorAll('audio');
for (var i = 0; i < audios.length; i++) {
var audio = audios[i];
audio.pause();
}
}
function pauseCurrentVideo() {
var iframes = document.querySelectorAll('iframe');
for (var i = 0; i < iframes.length; i++) {
var oldFrame = iframes[i];
oldFrame.src = oldFrame.src;
}
}
-349
View File
@@ -1,349 +0,0 @@
var WRAUDIO_TEMPLATE = '<div id="wraudioid_xxx" class="audio_container">\
<div id="wraudio_button" class="audio_button"></div>\
<div class="audio_content">\
<p id="wraudio_title" class="audio_title"></p>\
<div class="audio_progress">\
<div class="audio_progress_bottom"></div>\
<div id="wraudio_progress_bar" class="audio_progress_top" style="width: 0;"></div>\
<div id="wraudio_progress_button" class="audio_progress_current" style="left: 0;"></div>\
</div>\
<div class="audio_time">\
<span id="wraudio_time_elapsed" class="audio_time_left"></span>\
<span id="wraudio_time_remains" class="audio_time_right"></span>\
</div>\
</div>\
</div>';
var _Utils = function () {
this.findChildById = function (element, childID) {
var retElement = null;
var lstChildren = Utils.getAllDescendant(element);
for (var i = 0; i < lstChildren.length; i++) {
if (lstChildren[i].id == childID) {
retElement = lstChildren[i];
break;
}
}
return retElement;
}
this.getAllDescendant = function (element, lstChildrenNodes) {
lstChildrenNodes = lstChildrenNodes ? lstChildrenNodes : [];
var lstChildren = element.childNodes;
for (var i = 0; i < lstChildren.length; i++) {
if (lstChildren[i].nodeType == 1) // 1 is 'ELEMENT_NODE'
{
lstChildrenNodes.push(lstChildren[i]);
lstChildrenNodes = Utils.getAllDescendant(lstChildren[i], lstChildrenNodes);
}
}
return lstChildrenNodes;
}
this._formatTime = function (time, quotationStyle) {
if (time < 0) {
return '- : -'
}
var hour = parseInt(time / 3600);
var min = parseInt((time - hour * 3600) / 60);
var sec = parseInt((time - hour * 3600 - min * 60));
var timeText = '';
if (hour > 0) {
timeText += hour + ':';
}
if (quotationStyle) {
if (min > 0) {
timeText += min + "'";
}
} else {
if (min < 10) {
timeText += '0' + min + ':';
} else {
timeText += min + ':';
}
}
if (quotationStyle) {
timeText += sec + "''";
} else {
if (sec < 10) {
timeText += '0' + sec;
} else {
timeText += sec;
}
}
return timeText;
}
this.formatTime = function (time, remains) {
var intTime = parseInt(time);
var desc = this._formatTime(intTime);
if (remains && intTime > 0) {
return '-' + desc;
}
return desc;
}
}
var Utils = new _Utils;
var player = {audio: null, current: null};
var videoIframes = [];
function getContentRoot() {
return document.getElementById('editor');
}
function createWRAudio(title, src, duration) {
var tmp = document.createElement('div');
tmp.innerHTML = WRAUDIO_TEMPLATE;
var wrAudio = tmp.firstChild;
wrAudio.id = 'wraudioid_' + Date.now() + '_' + Math.floor((Math.random() * 1000) + 1);
wrAudio.src = src;
wrAudio.duration = duration;
var titleEl = Utils.findChildById(wrAudio, 'wraudio_title', true);
titleEl.innerHTML = title || '';
var button = Utils.findChildById(wrAudio, 'wraudio_button', true);
button.addEventListener('click', function() {
clickPlayButton(wrAudio);
}, false);
var elapsedEl = Utils.findChildById(wrAudio, 'wraudio_time_elapsed', true);
var remainsEl = Utils.findChildById(wrAudio, 'wraudio_time_remains', true);
elapsedEl.innerHTML = Utils.formatTime(0);
remainsEl.innerHTML = Utils.formatTime(duration, true);
return wrAudio;
}
function resetWRAudio(wrAudio) {
if (!wrAudio) {
return;
}
var button = Utils.findChildById(wrAudio, 'wraudio_button', true);
button.classList.remove('active');
var elapsedEl = Utils.findChildById(wrAudio, 'wraudio_time_elapsed', true);
var remainsEl = Utils.findChildById(wrAudio, 'wraudio_time_remains', true);
elapsedEl.innerHTML = Utils.formatTime(0);
remainsEl.innerHTML = Utils.formatTime(wrAudio.duration, true);
var progressBar = Utils.findChildById(wrAudio, 'wraudio_progress_bar', true);
var progressButton = Utils.findChildById(wrAudio, 'wraudio_progress_button', true);
progressBar.style.width = '0';
progressButton.style.left = '0';
}
function clickPlayButton(wrAudio) {
if (!wrAudio) {
return;
}
var src = wrAudio.src;
if (!player.current) {
player.current = wrAudio;
player.audio.src = src;
player.audio.play();
} else if (player.current.id != wrAudio.id) {
var last = player.current;
player.current = wrAudio;
resetWRAudio(last);
player.audio.src = src;
player.audio.play();
} else {
if (!player.audio.paused) {
player.audio.pause();
} else {
player.audio.play();
}
}
}
function pauseCurrentAudio() {
if (player.audio) {
player.audio.pause();
}
}
function pauseCurrentVideo() {
let newVideoIframes = [];
for (var i = 0; i < videoIframes.length; i++) {
var oldFrame = videoIframes[i];
oldFrame.src = oldFrame.src;
}
}
function pauseMedia() {
pauseCurrentAudio();
pauseCurrentVideo();
}
function onPlay() {
var button = Utils.findChildById(player.current, 'wraudio_button', true);
button.classList.add('active');
}
function onPause() {
var button = Utils.findChildById(player.current, 'wraudio_button', true);
button.classList.remove('active');
}
function onEnded() {
var button = Utils.findChildById(player.current, 'wraudio_button', true);
button.classList.remove('active');
}
function onDurationchange() {
player.current.duration = player.audio.duration;
}
function onVideoPlaying() {
pauseCurrentAudio();
}
function onTimeupdate() {
var wrAudio = player.current;
var elapsedEl = Utils.findChildById(wrAudio, 'wraudio_time_elapsed', true);
var remainsEl = Utils.findChildById(wrAudio, 'wraudio_time_remains', true);
elapsedEl.innerHTML = Utils.formatTime(player.audio.currentTime);
remainsEl.innerHTML = Utils.formatTime(player.audio.duration - player.audio.currentTime, true);
var progressBar = Utils.findChildById(wrAudio, 'wraudio_progress_bar', true);
var progressButton = Utils.findChildById(wrAudio, 'wraudio_progress_button', true);
var progress = 0;
if (player.audio.duration > 0) {
progress = parseInt(player.audio.currentTime / player.audio.duration * 100);
}
progressBar.style.width = progress + '%';
progressButton.style.left = progress + '%';
}
function adjustMPContent() {
var imgs = document.querySelectorAll('img[data-src]');
for (var i = 0; i < imgs.length ; i++) {
var img = imgs[i];
img.src = img.getAttribute('data-src');
img.style = 'width: auto !important; height: auto !important; visibility: visible !important;max-width:100% !important';
}
var links = document.querySelectorAll('a[href^="https://open.weixin.qq.com/connect/oauth2/authorize"],a[href^="http://open.weixin.qq.com/connect/oauth2/authorize"]');
for (var i = 0; i < links.length ; i++) {
var link = links[i]
params = link.href.split('&');
for (var j = 0;j<params.length;j++) {
param = params[j];
if (param.startsWith('redirect_uri=')) {
link.href = decodeURIComponent(param.split('redirect_uri=')[1]);
}
}
}
var iframes = document.querySelectorAll('iframe[data-src]');
for (var i = 0; i < iframes.length ; i++) {
(function (index) {
var iframe = iframes[index];
var iframeWidth = document.body.offsetWidth;
var iframeHeight = iframeWidth * 0.75;
var src = iframe.getAttribute('data-src');
src = src.replace(/preview.html/, 'player.html');
src = src.replace(/&(width=.*height=.*)&/, '&width=' + iframeWidth + '&height=' + iframeHeight + '&');
iframe.src = src;
// 是否视频
/*
if ((' ' + iframe.className + ' ').indexOf(' video_iframe ') > -1) {
iframe.onload = function () {
var styleNode = document.createElement('style');
styleNode.type = 'text/css';
styleNode.appendChild(document.createTextNode(''));
iframe.contentDocument.body.appendChild(styleNode);
}
}
*/
// if (iframe.classList.contains("video_iframe")) {
// iframe.onload = function() {
// if (iframe.contentWindow && iframe.contentWindow.document) {
// var videoEl = iframe.contentWindow.document.getElementById("tenvideo_video_player_0");
// if (videoEl) {
// videoEl.onplaying = function() {
// onVideoPlaying();
// };
// }
// }
// };
// }
videoIframes.push(iframe);
})(i);
}
var votes = document.querySelectorAll(".vote_area");
for (var i = 0; i < votes.length; i++) {
var vote = votes[i];
vote.parentElement.removeChild(vote);
}
var miniPrograms = document.querySelectorAll("mp-miniprogram");
for (var i = 0; i < miniPrograms.length; i++) {
var miniProgram = miniPrograms[i];
var parentElement = miniProgram.parentElement;
if (parentElement && parentElement.tagName && parentElement.tagName.toLowerCase() === 'p') {
if (parentElement.parentElement) {
parentElement.parentElement.removeChild(parentElement);
}
}
}
var miniAppLinks = document.querySelectorAll(".weapp_text_link, .weapp_image_link");
for (var i = 0; i < miniAppLinks.length; i++) {
var miniAppLink = miniAppLinks[i];
miniAppLink.parentElement.removeChild(miniAppLink);
}
var needPlayer = false;
var mpvoices = document.querySelectorAll('mpvoice[voice_encode_fileid]');
for (var i = 0; i < mpvoices.length ; i++) {
var mpvoice = mpvoices[i];
var title = mpvoice.getAttribute('name');
var src = 'https://res.wx.qq.com/voice/getvoice?mediaid=' + mpvoice.getAttribute('voice_encode_fileid');
var duration = mpvoice.hasAttribute('play_length') ? mpvoice.getAttribute('play_length') / 1000 : -1;
var wrAudio = createWRAudio(title, src, duration);
mpvoice.parentElement.appendChild(wrAudio);
needPlayer = true;
}
var qqmusics = document.querySelectorAll('qqmusic[audiourl]');
for (var i = 0; i < qqmusics.length ; i++) {
var qqmusic = qqmusics[i];
var title = qqmusic.getAttribute('music_name');
var src = qqmusic.getAttribute('audiourl');
var duration = qqmusic.hasAttribute('play_length') ? qqmusic.getAttribute('play_length') / 1000 : -1;
var wrAudio = createWRAudio(title, src, duration);
qqmusic.parentElement.appendChild(wrAudio);
needPlayer = true;
}
if (needPlayer) {
var audio = document.createElement('audio');
audio.autoplay = false;
audio.controls = true;
audio.style.display = "none";
audio.addEventListener("play", onPlay, false);
audio.addEventListener("pause", onPause, false);
audio.addEventListener("ended", onEnded, false);
audio.addEventListener("durationchange", onDurationchange, false);
audio.addEventListener("timeupdate", onTimeupdate, false);
getContentRoot().appendChild(audio);
player.audio = audio;
}
}
document.addEventListener('DOMContentLoaded', function() {
adjustMPContent();
}, false);
-74
View File
@@ -1,74 +0,0 @@
(function() {
if (window.wrClickHandler) {
window.removeEventListener('click', window.wrClickHandler, true);
}
window.wrClickHandler = function(e) {
console.log('Hook: Click event intercepted', {
target: e.target,
tagName: e.target.tagName,
id: e.target.id,
className: e.target.className
});
var eventInfo = null;
var currentElement = e.target;
while (currentElement && currentElement !== document) {
var tagName = currentElement.tagName || '';
var currentId = currentElement.id || '';
if (currentId === 'js_name') {
// 公众号顶部的作者
eventInfo = {
contentType: 'profile',
bizId: ''
};
console.log('Hook: MP author click intercepted');
break;
} else if (currentId === 'copyright_info') {
// 公众号顶部的文章来源
var bizId = typeof source_encode_biz !== 'undefined' ? source_encode_biz : '';
if (bizId) {
eventInfo = {
contentType: 'profile',
bizId: bizId
};
console.log('Hook: Copyright profile click intercepted, bizId:', bizId);
}
break;
} else if (tagName === 'MP-COMMON-PROFILE') {
// 公众号
var bizId = currentElement.dataset && currentElement.dataset['id'] ? currentElement.dataset['id'] : '';
if (bizId) {
eventInfo = {
contentType: 'profile',
bizId: bizId
};
console.log('Hook: Profile click intercepted, bizId:', bizId);
}
break;
} else if (currentElement.href && currentElement.href.indexOf('mp.weixin.qq.com') !== -1) {
// 公众号文章链接
eventInfo = {
contentType: 'link',
url: currentElement.href
};
console.log('Hook: Link click intercepted, URL:', currentElement.href);
break;
}
currentElement = currentElement.parentElement;
}
// 如果有 eventInfo,则阻断事件冒泡,拦截公众号原始的跳转弹窗
if (eventInfo) {
e.preventDefault();
e.stopPropagation();
}
if (eventInfo && wereadBridge) {
wereadBridge.execMPReaderMethod('MPReader', {
cmd: 'clickMpInterceptor',
data: eventInfo
});
}
};
window.addEventListener('click', window.wrClickHandler, true);
console.log('Hook: Click interceptor setup completed');
})();
@@ -1,16 +0,0 @@
window.newsInjector = {
removeUnnecessary: function() {
document.getElementsByTagName('body')[0].style = 'background-color:red'
var divs = document.getElementsByTagName('div')
for (var i = divs.length - 1; i >= 0; i--) {
var node = divs[i]
if (node.textContent == '查看全文' &&
node.nextSibling &&
node.nextSibling.nodeName == 'IMG') {
node.style = 'display: none!important;'
node.nextSibling.style = 'display: none!important';
}
}
}
}