Epub阅读器0.0.1

This commit is contained in:
shen
2026-05-21 19:40:51 +08:00
commit daa36d8fe7
559 changed files with 106266 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
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
@@ -0,0 +1,26 @@
(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
@@ -0,0 +1,125 @@
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;
}
}
File diff suppressed because it is too large Load Diff
+237
View File
@@ -0,0 +1,237 @@
(function() /** {$b5167ccadfb66ea3ba95d92d7c9946c9} */{
WeReadBridge = function() /** {$b9c7abd4d524a31563f1f27b768c47a9} */ {
this._sendMessageQueue = [];
this._callback_count = 1000;
this._callback_map = {};
this.available_func = {};
this._iframe = document.createElement("iframe");
this._iframe.setAttribute("id", "iframe");
this._iframe.setAttribute("style","position:absolute;top:0;left:0;width:1px;height:1px;visibility:hidden;");
this._QUEUE_HAS_MESSAGE_URL = 'wereadapijs://dispatch_message/';
document.body.appendChild(this._iframe);
this._resultIframe = document.createElement("iframe");
this._resultIframe.setAttribute("id", "_resultIframe");
this._resultIframe.setAttribute("style","position:absolute;top:0;left:0;width:1px;height:1px;visibility:hidden;");
this._resultIframe._SET_RESULT_URL = 'wereadapijs://private/setresult/';
document.body.appendChild(this._resultIframe);
}
window.callback = {}
function wrapCallback(name, callback) {
window.callback[name] = callback;
return name;
}
WeReadBridge.prototype.handleWithRichEditor = function(apiName, params,successCallback, failCallback) {
//alert("hi");
this._call(apiName,params,function(successOrNot, result) {
if (typeof successCallback === "function" || typeof failCallback === "function") {
self._handleCallback(result, successOrNot, successCallback, failCallback);
};
});
}
WeReadBridge.prototype.execMPReaderMethod = function(apiName, params, successCallback, failCallback) {
this._call(apiName,params,function(successOrNot, result) {
if (typeof successCallback === "function" || typeof failCallback === "function") {
self._handleCallback(result, successOrNot, successCallback, failCallback);
};
});
}
WeReadBridge.prototype.fetchQueue = function() /** {$4a202e1afc4b36610e87f574e5478e15} */ {
var messageQueueString = JSON.stringify(this._sendMessageQueue);
this._sendMessageQueue = [];
this._setResultValue('fetchqueue', messageQueueString);
return messageQueueString;
}
WeReadBridge.prototype._setResultValue = function(scene, result) /** {$23e4b67694084da787d5fa5c739b9a66} */ {
// Android 通过另一个iframe上传数据
if (result === undefined) {
result = '';
}
this._resultIframe.src = this._resultIframe._SET_RESULT_URL + scene + '&' + this._base64Encode(this._utf8Encode(result));
}
// public method for url encoding
WeReadBridge.prototype._utf8Encode = function(str) /** {$9da9355c0a506c2d0c6dd40c35ea7fb6} */ {
str = str.replace(/\r\n/g, "\n");
var utftext = "";
for (var n = 0; n < str.length; n++) {
var c = str.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
WeReadBridge.prototype._base64Encode = function(str) /** {$caa95e8456b5cda1716514776a0bf77a} */ {
//base64编码
var base64encodechars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
if (str === undefined) {
return str;
}
var out, i, len;
var c1, c2, c3;
len = str.length;
i = 0;
out = "";
while (i < len) {
c1 = str.charCodeAt(i++) & 0xff;
if (i == len) {
out += base64encodechars.charAt(c1 >> 2);
out += base64encodechars.charAt((c1 & 0x3) << 4);
out += "==";
break;
}
c2 = str.charCodeAt(i++);
if (i == len) {
out += base64encodechars.charAt(c1 >> 2);
out += base64encodechars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4));
out += base64encodechars.charAt((c2 & 0xf) << 2);
out += "=";
break;
}
c3 = str.charCodeAt(i++);
out += base64encodechars.charAt(c1 >> 2);
out += base64encodechars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4));
out += base64encodechars.charAt(((c2 & 0xf) << 2) | ((c3 & 0xc0) >> 6));
out += base64encodechars.charAt(c3 & 0x3f);
}
return out;
}
WeReadBridge.prototype._sendMessage = function(message) /** {$0685a6a5b33dbf2f615a5ae1faedb0c8} */ {
this._sendMessageQueue.push(message);
this._iframe.src = this._QUEUE_HAS_MESSAGE_URL;
}
WeReadBridge.prototype.handleMessage = function(message) /** {$623f3ca7480b38b659e4f32c858046f4} */ {
var callbackId = message["callbackId"];
if (!callbackId || typeof callbackId !== 'string') {
return;
}
var successOrNot = message["successOrNot"];
var params = message["params"];
if (typeof this._callback_map[callbackId] === "function"){
this._callback_map[callbackId](successOrNot,params);
delete this._callback_map[callbackId];
}
}
WeReadBridge.prototype._call = function(func, params, callback) /** {$0a78d8c17b87d24c599c91b498b8662b} */ {
if (!func || typeof func !== "string") {
return;
}
if (typeof params !== "object") {
params = {};
}
var msgObj = {"func":func,"params":params};
var callbackID = (this._callback_count++).toString();
params["callbackId"] = callbackID;
if (typeof callback === "function") {
this._callback_map[callbackID] = callback;
msgObj["callbackId"] = callbackID;
}
this._sendMessage(JSON.stringify(msgObj));
}
WeReadBridge.prototype._handleCallback = function(result, successOrNot, successCallback, failCallback) {
// Android 返回 JSON String,在这里转JSON
resultJSON = (typeof result == 'string') ? JSON.parse(result) : result;
if (successOrNot) {
successCallback(resultJSON);
} else {
failCallback(resultJSON);
}
}
WeReadBridge.prototype.onReady = function(func) /** {$9ce323af53931aa37c8788958d68f687} */ {
!_isReady && _bindReadyFuns.unshift([this, func]);
}
WeReadBridge.prototype.bindReady = function(func) /** {$d3b152348187da57f207a3a3eb27fa54} */ {
!_isReady ? _bindReadyFuns.unshift([this, func]) : func.call(this);
}
WeReadBridge.prototype.isReady = function() /** {$9c23e94ea3f2aafaae1c349574569f78} */ {
return _isReady;
}
WeReadBridge.prototype.isAvailable = function(apiName) /** {$67e34482192a8170c150ec414ed4ba7a} */ {
return !!(_WeReadBridgeInfo && _WeReadBridgeInfo["apis"][apiName]);
}
var _isReady, _WeReadBridgeInfo, _bindReadyFuns = [],
_onReady = function() /** {$d1b53430a7b4de2d74d12a81eec715ec} */ {
_WeReadBridgeInfo = window["__QMB_INFO__"];
if (_isReady) return;
_isReady = true;
var _funcParams;
while (_funcParams = _bindReadyFuns.pop()) {
_funcParams[1].call(_funcParams[0]);
};
};
window["wereadBridge"] = new WeReadBridge();
if (window["__WRB_INFO__"]) {
_onReady();
} else {
window["__WRB_INFO_CALL__"] = function() /** {$4f54cf9d496e3e999912adfb7b5d37b7} */ {
_onReady();
};
}
})();
// /** 函数模板
// * func_name description
// * @param successCallback(result) result["param_name"]
// * @param failCallback(result) result["param_name"]
// */
// WeReadBridge.prototype.func_name = function(params, successCallback, failCallback) {
// var self = this;
// this._call("func_name", params, function(successOrNot, result) {
// var params = {"func_name":result};
// self.localLog(params);
// if (successOrNot) {
// successCallback(result);
// } else {
// failCallback(result);
// }
// });
// }
// window.wereadBridge = new WeReadBridge();
// window.wereadBridge.goToUrl("http://wecall.qq.com/");//weread://bookDetail?opentype=0&bookId=414048");
/**
load页面之前需要先执行以下的js
eval('window["__QMB_INFO__"]={apis:{"a":1,"b":1},ver:"4.0.5",os:"android"};window["__QMB_INFO_CALL__"]&&window["__QMB_INFO_CALL__"]();');
*/
// window.wereadBridge.moreOperation(new Array({'shareToWechatTimeLine': {'title':'title','imageUrl':'imageUrl','abstract':'abstract','url':'url'}}));
// window.wereadBridge.shareToWechatFriend({'title':'中文','imageUrl':'imageUrl','abstract':'中文测试','url':'url'}, function(){}, function(){});
// window.wereadBridge.shareToWechatTimeline({'title':'中文','imageUrl':'imageUrl','abstract':'中文测试','url':'url'}, function(){}, function(){});
// window.wereadBridge.getAppInfo(function(result){alert('getAppInfo: onSucc:'+JSON.stringify(result));}, function(result){alert('getAppInfo: onFailed:'+JSON.stringify(result));});
// window.wereadBridge.closeBrowser();
// window.wereadBridge.window.wereadBridge.mobileSync(function(error,result) {alert('error:' + error+"\n"+'result:' + result);});;
// window.wereadBridge.window.wereadBridge.refreshToken(function(error,result) {alert('error:' + error+"\n"+'result:' + result);});;
// window.wereadBridge.showBrowserMoreButton({'shareToWechatFriend':0, 'shareToWechatTimeline':1, 'copyLink':0, 'openLinkWithBrowser':0});
File diff suppressed because one or more lines are too long
@@ -0,0 +1,261 @@
/**
* Copyright (C) 2015 Wasabeef
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var RDisplay = {};
RDisplay.editor = document.getElementById('editor');
RDisplay.dataSeparator = "r_e_ds";
// Initializations
RDisplay.onBookToucheStart = function(ele){
ele.classList.add('re_bookItem_Touched');
}
RDisplay.onBookToucheEnd = function(ele){
ele.classList.remove('re_bookItem_Touched');
}
RDisplay.clickedBookId = null
RDisplay.onBookClick = function(ele){
var id = ele.getAttribute('data-id');
wereadBridge.handleWithRichEditor("onGotoBookDetail", {"data-id" : id}, "", "");
}
RDisplay.getClickedBookPosition = function() {
if(RDisplay.clickedBookId) {
var ele = document.getElementById(RDisplay.clickedBookId);
var position = RDisplay.getCoords(ele)
var position = {"top":position.top,"height":position.height,"width":position.width,"left":position.left}
var result = JSON.stringify(position)
return result
}
}
RDisplay.onImageClick = function(ele){
var src = ele.getAttribute('src');
var index = ele.getAttribute('index')
RDisplay.clickedBookId = ele.getAttribute('id');
var position = RDisplay.getCoords(ele)
wereadBridge.handleWithRichEditor("onGotoImageDetail", {"src" : src,"index":index,"top":position.top,"height":position.height,"width":position.width,"left":position.left}, "", "");
}
var reBookItems = document.querySelectorAll('.re_bookItem');
var reImgs = document.querySelectorAll('.re_img');
if(reBookItems.length > 0){
for(var i = 0; i<reBookItems.length; i++){
var bookItem = reBookItems[i];
bookItem.setAttribute('ontouchstart', 'RDisplay.onBookToucheStart(this)');
bookItem.setAttribute('ontouchend', 'RDisplay.onBookToucheEnd(this)');
bookItem.setAttribute('ontouchcancel', 'RDisplay.onBookToucheEnd(this)');
bookItem.setAttribute('onclick', 'RDisplay.onBookClick(this)');
}
}
if(reImgs.length > 0){
for(var i = 0; i<reImgs.length; i++){
var img = reImgs[i];
img.setAttribute('onclick', 'RDisplay.onImageClick(this)');
img.setAttribute('index', i.toString());
var idStr = "re_img_" + i
img.setAttribute('id',idStr);
}
}
RDisplay.getAllImgSrcs = function() {
srcArray = [];
var imgs = document.querySelectorAll('.re_img');
for (var i = 0; i < imgs.length; i++) {
srcArray.push(imgs[i].getAttribute('src'))
}
var srcs = "";
if (srcArray.length > 0) {
srcs = srcArray.join(RDisplay.dataSeparator);
}
return srcs;
}
// 获取当前光标的node
RDisplay.getSelectedNode = function() {
var node = RDisplay.getSelectedBaseNode();
if (node) { return (node.nodeName == "#text" ? node.parentNode : node); }
};
// 获取当前光标的node
RDisplay.getSelectedBaseNode = function() {
var node, selection;
if (window.getSelection) {
selection = getSelection();
node = selection.anchorNode; // 返回该选区起点所在的节点(Node)
}
if (!node && document.selection) {
selection = document.selection
var range = selection.getRangeAt ? selection.getRangeAt(0) : selection.createRange();
node = range.commonAncestorContainer ? range.commonAncestorContainer :
range.parentElement ? range.parentElement() : range.item(0);
}
return node;
};
RDisplay.setHtml = function(str){
document.getElementById('editor').innerHTML = str;
}
RDisplay.selected = function() {
RDisplay.editor.style.backgroundColor = 'rgba(238, 239, 241, 1)';
}
RDisplay.unselected = function() {
RDisplay.editor.style.backgroundColor = 'rgba(255, 255, 255, 1)';
}
RDisplay.logDom = null;
RDisplay.log = function(str){
if(RDisplay.logDom == null){
RDisplay.logDom = document.createElement('div');
RDisplay.logDom.setAttribute('style','position:absolute;top:0;right:0;font-size:12px;border:1px solid #000;color:#000;padding:3px');
document.getElementsByTagName('body')[0].appendChild(RDisplay.logDom);
}
RDisplay.logDom.innerHTML = RDisplay.logDom.innerHTML + "<br/>" + str;
}
document.addEventListener("selectionchange", function(e) {
RDisplay.contentChanged(e);
});
RDisplay.contentChanged = function(e) {
var param = {};
if (typeof(e) != "undefined") {
var node = RDisplay.getSelectedNode();
if (node) {
var nodeName = node.nodeName.toLowerCase();
// Link
if (nodeName == 'a') {
var title = node.getAttribute('title');
var href = node.getAttribute('href');
if (href != undefined) { param['link:'] = href; }
if (title !== undefined) { param['link-title'] = title; }
param['link-text'] = node.innerText;
}
}
}
wereadBridge.handleWithRichEditor("onSelectionChange",param, "", "");
}
RDisplay.getCoords = function(elem) {
var box = elem.getBoundingClientRect();
var body = document.body;
var docEl = document.documentElement;
var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
var clientTop = docEl.clientTop || body.clientTop || 0;
var clientLeft = docEl.clientLeft || body.clientLeft || 0;
var top = box.top + scrollTop - clientTop;
var left = box.left + scrollLeft - clientLeft;
return {
top: Math.round(top),
left: Math.round(left),
height: box.height,
width: box.width
};
}
RDisplay.getLastImgSrc = function(){
var imgs = document.getElementsByTagName('img');
for(var i = imgs.length - 1;i >= 0;i--){
var img = imgs[i];
var src = img.getAttribute('src');
if(src && (src.indexOf('http:') == 0 || src.indexOf('https:') == 0 || src.indexOf('wrcachedwebviewurlkey-http:') == 0 || src.indexOf('wrcachedwebviewurlkey-https:') == 0)){
return src;
}
var data_src = img.getAttribute('data-src');
if(data_src && (data_src.indexOf('http:') == 0 || data_src.indexOf('https:') == 0 || data_src.indexOf('wrcachedwebviewurlkey-http:') == 0 || data_src.indexOf('wrcachedwebviewurlkey-https:') == 0)){
return data_src;
}
}
return '';
}
RDisplay.removeImgWithSrc = function(url) {
var imgs = document.getElementsByTagName('img');
for (var i = imgs.length - 1; i >= 0; i--) {
var img = imgs[i]
var data_src = img.getAttribute('data-src');
var src = img.getAttribute('src');
if(data_src === url || src === url) {
img.parentNode.removeChild(img);
}
}
}
RDisplay.getText = function() {
var tempDom = document.createElement('div');
// 过滤掉插入的书dom里的文字,这里需要过滤掉零宽连字符,不然牛逼的ios里面会出现乱码
tempDom.innerHTML = RDisplay.editor.innerHTML.replace(new RegExp('\u200D', 'g'), '')
.replace('<br>', '')
.replace('<br />', '');
// getText时将插入的书籍和图片转化成 [书籍] [图片]
var books = tempDom.querySelectorAll('.re_bookItem'),
imgs = tempDom.querySelectorAll('.re_img');
if(books.length > 0){
for(var i=0; i<books.length; i++){
var bookDom = books[i],
bookTextNode = document.createTextNode('[书籍]');
bookDom.parentNode.insertBefore(bookTextNode, bookDom);
bookDom.parentNode.removeChild(bookDom);
}
}
if(imgs.length > 0){
for(var i=0; i<imgs.length; i++){
var imgDom = imgs[i],
imgTextNode = document.createTextNode('[图片]');
imgDom.parentNode.insertBefore(imgTextNode, imgDom);
imgDom.parentNode.removeChild(imgDom);
}
}
return tempDom.innerText;
}
RDisplay.getHtmlForEpub = function() {
return RDisplay.editor.innerHTML;
}
RDisplay.getAllBookIds = function() {
var books = RE.editor.querySelectorAll('.re_bookItem'),
idArray = [];
for (var i = 0; i < books.length; i++) {
idArray.push(books[i].getAttribute('data-id'))
}
var bookIds = "";
if (idArray.length > 0) {
bookIds = idArray.join(RE.dataSeparator);
}
return bookIds;
}
document.addEventListener("DOMContentLoaded", function(event) {
wereadBridge.handleWithRichEditor("onDOMContentLoaded", "", "", "");
});
File diff suppressed because it is too large Load Diff
+766
View File
@@ -0,0 +1,766 @@
/*
The MIT License (MIT)
Copyright (c) 2007-2013 Einar Lielmanis and contributors.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
JS Beautifier
---------------
*/
function trim(s) {
return s.replace(/^\s+|\s+$/g, '');
}
function ltrim(s) {
return s.replace(/^\s+/g, '');
}
function style_html(html_source, options) {
//Wrapper function to invoke all the necessary constructors and deal with the output.
var multi_parser,
indent_inner_html,
indent_size,
indent_character,
wrap_line_length,
brace_style,
unformatted,
preserve_newlines,
max_preserve_newlines;
options = options || {};
// backwards compatibility to 1.3.4
if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) &&
(options.max_char === undefined || parseInt(options.max_char, 10) === 0)) {
options.wrap_line_length = options.max_char;
}
indent_inner_html = options.indent_inner_html || false;
indent_size = parseInt(options.indent_size || 4, 10);
indent_character = options.indent_char || ' ';
brace_style = options.brace_style || 'collapse';
wrap_line_length = parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10);
unformatted = options.unformatted || ['a', 'span', 'bdo', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'q', 'sub', 'sup', 'tt', 'i', 'b', 'big', 'small', 'u', 's', 'strike', 'font', 'ins', 'del', 'pre', 'address', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
preserve_newlines = options.preserve_newlines || true;
max_preserve_newlines = preserve_newlines ? parseInt(options.max_preserve_newlines || 32786, 10) : 0;
indent_handlebars = options.indent_handlebars || false;
function Parser() {
this.pos = 0; //Parser position
this.token = '';
this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
parent: 'parent1',
parentcount: 1,
parent1: ''
};
this.tag_type = '';
this.token_text = this.last_token = this.last_text = this.token_type = '';
this.newlines = 0;
this.indent_content = indent_inner_html;
this.Utils = { //Uilities made available to the various functions
whitespace: "\n\r\t ".split(''),
single_token: 'br,input,link,meta,!doctype,basefont,base,area,hr,wbr,param,img,isindex,?xml,embed,?php,?,?='.split(','), //all the single tags for HTML
extra_liners: 'head,body,/html'.split(','), //for tags that need a line of whitespace before them
in_array: function(what, arr) {
for (var i = 0; i < arr.length; i++) {
if (what === arr[i]) {
return true;
}
}
return false;
}
};
this.traverse_whitespace = function() {
var input_char = '';
input_char = this.input.charAt(this.pos);
if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
this.newlines = 0;
while (this.Utils.in_array(input_char, this.Utils.whitespace)) {
if (preserve_newlines && input_char === '\n' && this.newlines <= max_preserve_newlines) {
this.newlines += 1;
}
this.pos++;
input_char = this.input.charAt(this.pos);
}
return true;
}
return false;
};
this.get_content = function() { //function to capture regular content between tags
var input_char = '',
content = [],
space = false; //if a space is needed
while (this.input.charAt(this.pos) !== '<') {
if (this.pos >= this.input.length) {
return content.length ? content.join('') : ['', 'TK_EOF'];
}
if (this.traverse_whitespace()) {
if (content.length) {
space = true;
}
continue; //don't want to insert unnecessary space
}
if (indent_handlebars) {
// Handlebars parsing is complicated.
// {{#foo}} and {{/foo}} are formatted tags.
// {{something}} should get treated as content, except:
// {{else}} specifically behaves like {{#if}} and {{/if}}
var peek3 = this.input.substr(this.pos, 3);
if (peek3 === '{{#' || peek3 === '{{/') {
// These are tags and not content.
break;
} else if (this.input.substr(this.pos, 2) === '{{') {
if (this.get_tag(true) === '{{else}}') {
break;
}
}
}
input_char = this.input.charAt(this.pos);
this.pos++;
if (space) {
if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached
this.print_newline(false, content);
this.print_indentation(content);
} else {
this.line_char_count++;
content.push(' ');
}
space = false;
}
this.line_char_count++;
content.push(input_char); //letter at-a-time (or string) inserted to an array
}
return content.length ? content.join('') : '';
};
this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify
if (this.pos === this.input.length) {
return ['', 'TK_EOF'];
}
var input_char = '';
var content = '';
var reg_match = new RegExp('</' + name + '\\s*>', 'igm');
reg_match.lastIndex = this.pos;
var reg_array = reg_match.exec(this.input);
var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script
if (this.pos < end_script) { //get everything in between the script tags
content = this.input.substring(this.pos, end_script);
this.pos = end_script;
}
return content;
};
this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object
if (this.tags[tag + 'count']) { //check for the existence of this tag type
this.tags[tag + 'count']++;
this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
} else { //otherwise initialize this tag type
this.tags[tag + 'count'] = 1;
this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
}
this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent)
this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
};
this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer
if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
var temp_parent = this.tags.parent; //check to see if it's a closable tag.
while (temp_parent) { //till we reach '' (the initial value);
if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it
break;
}
temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
}
if (temp_parent) { //if we caught something
this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
}
delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
if (this.tags[tag + 'count'] === 1) {
delete this.tags[tag + 'count'];
} else {
this.tags[tag + 'count']--;
}
}
};
this.indent_to_tag = function(tag) {
// Match the indentation level to the last use of this tag, but don't remove it.
if (!this.tags[tag + 'count']) {
return;
}
var temp_parent = this.tags.parent;
while (temp_parent) {
if (tag + this.tags[tag + 'count'] === temp_parent) {
break;
}
temp_parent = this.tags[temp_parent + 'parent'];
}
if (temp_parent) {
this.indent_level = this.tags[tag + this.tags[tag + 'count']];
}
};
this.get_tag = function(peek) { //function to get a full tag and parse its type
var input_char = '',
content = [],
comment = '',
space = false,
tag_start, tag_end,
tag_start_char,
orig_pos = this.pos,
orig_line_char_count = this.line_char_count;
peek = peek !== undefined ? peek : false;
do {
if (this.pos >= this.input.length) {
if (peek) {
this.pos = orig_pos;
this.line_char_count = orig_line_char_count;
}
return content.length ? content.join('') : ['', 'TK_EOF'];
}
input_char = this.input.charAt(this.pos);
this.pos++;
if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
space = true;
continue;
}
if (input_char === "'" || input_char === '"') {
input_char += this.get_unformatted(input_char);
space = true;
}
if (input_char === '=') { //no space before =
space = false;
}
if (content.length && content[content.length - 1] !== '=' && input_char !== '>' && space) {
//no space after = or before >
if (this.line_char_count >= this.wrap_line_length) {
this.print_newline(false, content);
this.print_indentation(content);
} else {
content.push(' ');
this.line_char_count++;
}
space = false;
}
if (indent_handlebars && tag_start_char === '<') {
// When inside an angle-bracket tag, put spaces around
// handlebars not inside of strings.
if ((input_char + this.input.charAt(this.pos)) === '{{') {
input_char += this.get_unformatted('}}');
if (content.length && content[content.length - 1] !== ' ' && content[content.length - 1] !== '<') {
input_char = ' ' + input_char;
}
space = true;
}
}
if (input_char === '<' && !tag_start_char) {
tag_start = this.pos - 1;
tag_start_char = '<';
}
if (indent_handlebars && !tag_start_char) {
if (content.length >= 2 && content[content.length - 1] === '{' && content[content.length - 2] == '{') {
if (input_char === '#' || input_char === '/') {
tag_start = this.pos - 3;
} else {
tag_start = this.pos - 2;
}
tag_start_char = '{';
}
}
this.line_char_count++;
content.push(input_char); //inserts character at-a-time (or string)
if (content[1] && content[1] === '!') { //if we're in a comment, do something special
// We treat all comments as literals, even more than preformatted tags
// we just look for the appropriate close tag
content = [this.get_comment(tag_start)];
break;
}
if (indent_handlebars && tag_start_char === '{' && content.length > 2 && content[content.length - 2] === '}' && content[content.length - 1] === '}') {
break;
}
} while (input_char !== '>');
var tag_complete = content.join('');
var tag_index;
var tag_offset;
if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends
tag_index = tag_complete.indexOf(' ');
} else if (tag_complete[0] === '{') {
tag_index = tag_complete.indexOf('}');
} else { //otherwise go with the tag ending
tag_index = tag_complete.indexOf('>');
}
if (tag_complete[0] === '<' || !indent_handlebars) {
tag_offset = 1;
} else {
tag_offset = tag_complete[2] === '#' ? 3 : 2;
}
var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase();
if (tag_complete.charAt(tag_complete.length - 2) === '/' ||
this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /)
if (!peek) {
this.tag_type = 'SINGLE';
}
} else if (indent_handlebars && tag_complete[0] === '{' && tag_check === 'else') {
if (!peek) {
this.indent_to_tag('if');
this.tag_type = 'HANDLEBARS_ELSE';
this.indent_content = true;
this.traverse_whitespace();
}
} else if (tag_check === 'script') { //for later script handling
if (!peek) {
this.record_tag(tag_check);
this.tag_type = 'SCRIPT';
}
} else if (tag_check === 'style') { //for future style handling (for now it justs uses get_content)
if (!peek) {
this.record_tag(tag_check);
this.tag_type = 'STYLE';
}
} else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags
comment = this.get_unformatted('</' + tag_check + '>', tag_complete); //...delegate to get_unformatted function
content.push(comment);
// Preserve collapsed whitespace either before or after this tag.
if (tag_start > 0 && this.Utils.in_array(this.input.charAt(tag_start - 1), this.Utils.whitespace)) {
content.splice(0, 0, this.input.charAt(tag_start - 1));
}
tag_end = this.pos - 1;
if (this.Utils.in_array(this.input.charAt(tag_end + 1), this.Utils.whitespace)) {
content.push(this.input.charAt(tag_end + 1));
}
this.tag_type = 'SINGLE';
} else if (tag_check.charAt(0) === '!') { //peek for <! comment
// for comments content is already correct.
if (!peek) {
this.tag_type = 'SINGLE';
this.traverse_whitespace();
}
} else if (!peek) {
if (tag_check.charAt(0) === '/') { //this tag is a double tag so check for tag-ending
this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
this.tag_type = 'END';
this.traverse_whitespace();
} else { //otherwise it's a start-tag
this.record_tag(tag_check); //push it on the tag stack
if (tag_check.toLowerCase() !== 'html') {
this.indent_content = true;
}
this.tag_type = 'START';
// Allow preserving of newlines after a start tag
this.traverse_whitespace();
}
if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line
this.print_newline(false, this.output);
if (this.output.length && this.output[this.output.length - 2] !== '\n') {
this.print_newline(true, this.output);
}
}
}
if (peek) {
this.pos = orig_pos;
this.line_char_count = orig_line_char_count;
}
return content.join(''); //returns fully formatted tag
};
this.get_comment = function(start_pos) { //function to return comment content in its entirety
// this is will have very poor perf, but will work for now.
var comment = '',
delimiter = '>',
matched = false;
this.pos = start_pos;
input_char = this.input.charAt(this.pos);
this.pos++;
while (this.pos <= this.input.length) {
comment += input_char;
// only need to check for the delimiter if the last chars match
if (comment[comment.length - 1] === delimiter[delimiter.length - 1] &&
comment.indexOf(delimiter) !== -1) {
break;
}
// only need to search for custom delimiter for the first few characters
if (!matched && comment.length < 10) {
if (comment.indexOf('<![if') === 0) { //peek for <![if conditional comment
delimiter = '<![endif]>';
matched = true;
} else if (comment.indexOf('<![cdata[') === 0) { //if it's a <[cdata[ comment...
delimiter = ']]>';
matched = true;
} else if (comment.indexOf('<![') === 0) { // some other ![ comment? ...
delimiter = ']>';
matched = true;
} else if (comment.indexOf('<!--') === 0) { // <!-- comment ...
delimiter = '-->';
matched = true;
}
}
input_char = this.input.charAt(this.pos);
this.pos++;
}
return comment;
};
this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety
if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) !== -1) {
return '';
}
var input_char = '';
var content = '';
var min_index = 0;
var space = true;
do {
if (this.pos >= this.input.length) {
return content;
}
input_char = this.input.charAt(this.pos);
this.pos++;
if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
if (!space) {
this.line_char_count--;
continue;
}
if (input_char === '\n' || input_char === '\r') {
content += '\n';
/* Don't change tab indention for unformatted blocks. If using code for html editing, this will greatly affect <pre> tags if they are specified in the 'unformatted array'
for (var i=0; i<this.indent_level; i++) {
content += this.indent_string;
}
space = false; //...and make sure other indentation is erased
*/
this.line_char_count = 0;
continue;
}
}
content += input_char;
this.line_char_count++;
space = true;
if (indent_handlebars && input_char === '{' && content.length && content[content.length - 2] === '{') {
// Handlebars expressions in strings should also be unformatted.
content += this.get_unformatted('}}');
// These expressions are opaque. Ignore delimiters found in them.
min_index = content.length;
}
} while (content.toLowerCase().indexOf(delimiter, min_index) === -1);
return content;
};
this.get_token = function() { //initial handler for token-retrieval
var token;
if (this.last_token === 'TK_TAG_SCRIPT' || this.last_token === 'TK_TAG_STYLE') { //check if we need to format javascript
var type = this.last_token.substr(7);
token = this.get_contents_to(type);
if (typeof token !== 'string') {
return token;
}
return [token, 'TK_' + type];
}
if (this.current_mode === 'CONTENT') {
token = this.get_content();
if (typeof token !== 'string') {
return token;
} else {
return [token, 'TK_CONTENT'];
}
}
if (this.current_mode === 'TAG') {
token = this.get_tag();
if (typeof token !== 'string') {
return token;
} else {
var tag_name_type = 'TK_TAG_' + this.tag_type;
return [token, tag_name_type];
}
}
};
this.get_full_indent = function(level) {
level = this.indent_level + level || 0;
if (level < 1) {
return '';
}
return Array(level + 1).join(this.indent_string);
};
this.is_unformatted = function(tag_check, unformatted) {
//is this an HTML5 block-level link?
if (!this.Utils.in_array(tag_check, unformatted)) {
return false;
}
if (tag_check.toLowerCase() !== 'a' || !this.Utils.in_array('a', unformatted)) {
return true;
}
//at this point we have an tag; is its first child something we want to remain
//unformatted?
var next_tag = this.get_tag(true /* peek. */ );
// test next_tag to see if it is just html tag (no external content)
var tag = (next_tag || "").match(/^\s*<\s*\/?([a-z]*)\s*[^>]*>\s*$/);
// if next_tag comes back but is not an isolated tag, then
// let's treat the 'a' tag as having content
// and respect the unformatted option
if (!tag || this.Utils.in_array(tag, unformatted)) {
return true;
} else {
return false;
}
};
this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions
this.input = js_source || ''; //gets the input for the Parser
this.output = [];
this.indent_character = indent_character;
this.indent_string = '';
this.indent_size = indent_size;
this.brace_style = brace_style;
this.indent_level = 0;
this.wrap_line_length = wrap_line_length;
this.line_char_count = 0; //count to see if wrap_line_length was exceeded
for (var i = 0; i < this.indent_size; i++) {
this.indent_string += this.indent_character;
}
this.print_newline = function(force, arr) {
this.line_char_count = 0;
if (!arr || !arr.length) {
return;
}
if (force || (arr[arr.length - 1] !== '\n')) { //we might want the extra line
arr.push('\n');
}
};
this.print_indentation = function(arr) {
for (var i = 0; i < this.indent_level; i++) {
arr.push(this.indent_string);
this.line_char_count += this.indent_string.length;
}
};
this.print_token = function(text) {
if (text || text !== '') {
if (this.output.length && this.output[this.output.length - 1] === '\n') {
this.print_indentation(this.output);
text = ltrim(text);
}
}
this.print_token_raw(text);
};
this.print_token_raw = function(text) {
if (text && text !== '') {
if (text.length > 1 && text[text.length - 1] === '\n') {
// unformatted tags can grab newlines as their last character
this.output.push(text.slice(0, -1));
this.print_newline(false, this.output);
} else {
this.output.push(text);
}
}
for (var n = 0; n < this.newlines; n++) {
this.print_newline(n > 0, this.output);
}
this.newlines = 0;
};
this.indent = function() {
this.indent_level++;
};
this.unindent = function() {
if (this.indent_level > 0) {
this.indent_level--;
}
};
};
return this;
}
/*_____________________--------------------_____________________*/
multi_parser = new Parser(); //wrapping functions Parser
multi_parser.printer(html_source, indent_character, indent_size, wrap_line_length, brace_style); //initialize starting values
while (true) {
var t = multi_parser.get_token();
multi_parser.token_text = t[0];
multi_parser.token_type = t[1];
if (multi_parser.token_type === 'TK_EOF') {
break;
}
switch (multi_parser.token_type) {
case 'TK_TAG_START':
multi_parser.print_newline(false, multi_parser.output);
multi_parser.print_token(multi_parser.token_text);
if (multi_parser.indent_content) {
multi_parser.indent();
multi_parser.indent_content = false;
}
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_STYLE':
case 'TK_TAG_SCRIPT':
multi_parser.print_newline(false, multi_parser.output);
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_END':
//Print new line only if the tag has no content and has child
if (multi_parser.last_token === 'TK_CONTENT' && multi_parser.last_text === '') {
var tag_name = multi_parser.token_text.match(/\w+/)[0];
var tag_extracted_from_last_output = null;
if (multi_parser.output.length) {
tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length - 1].match(/(?:<|{{#)\s*(\w+)/);
}
if (tag_extracted_from_last_output === null ||
tag_extracted_from_last_output[1] !== tag_name) {
multi_parser.print_newline(false, multi_parser.output);
}
}
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_SINGLE':
// Don't add a newline before elements that should remain unformatted.
var tag_check = multi_parser.token_text.match(/^\s*<([a-z]+)/i);
if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)) {
multi_parser.print_newline(false, multi_parser.output);
}
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_HANDLEBARS_ELSE':
multi_parser.print_token(multi_parser.token_text);
if (multi_parser.indent_content) {
multi_parser.indent();
multi_parser.indent_content = false;
}
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_CONTENT':
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'TAG';
break;
case 'TK_STYLE':
case 'TK_SCRIPT':
if (multi_parser.token_text !== '') {
multi_parser.print_newline(false, multi_parser.output);
var text = multi_parser.token_text,
_beautifier,
script_indent_level = 1;
if (multi_parser.token_type === 'TK_SCRIPT') {
_beautifier = typeof js_beautify === 'function' && js_beautify;
} else if (multi_parser.token_type === 'TK_STYLE') {
_beautifier = typeof css_beautify === 'function' && css_beautify;
}
if (options.indent_scripts === "keep") {
script_indent_level = 0;
} else if (options.indent_scripts === "separate") {
script_indent_level = -multi_parser.indent_level;
}
var indentation = multi_parser.get_full_indent(script_indent_level);
if (_beautifier) {
// call the Beautifier if avaliable
text = _beautifier(text.replace(/^\s*/, indentation), options);
} else {
// simply indent the string otherwise
var white = text.match(/^\s*/)[0];
var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
var reindent = multi_parser.get_full_indent(script_indent_level - _level);
text = text.replace(/^\s*/, indentation)
.replace(/\r\n|\r|\n/g, '\n' + reindent)
.replace(/\s+$/, '');
}
if (text) {
multi_parser.print_token_raw(indentation + trim(text));
multi_parser.print_newline(false, multi_parser.output);
}
}
multi_parser.current_mode = 'TAG';
break;
}
multi_parser.last_token = multi_parser.token_type;
multi_parser.last_text = multi_parser.token_text;
}
return multi_parser.output.join('');
}
+25
View File
@@ -0,0 +1,25 @@
window.cssInjector = {
inject: function(json) {
Object.keys(json).forEach(function(identifier) {
var style = document.getElementById(identifier)
if (!style) {
var style = document.createElement('style');
style.type = 'text/css';
style.setAttribute('id', identifier);
}
style.innerHTML = json[identifier]
document.getElementsByTagName('head')[0].appendChild(style);
});
},
remove: function(identifier) {
var style = document.getElementById(identifier)
if (style) {
style.parentNode.removeChild(style)
}
},
}
File diff suppressed because one or more lines are too long
+349
View File
@@ -0,0 +1,349 @@
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
@@ -0,0 +1,74 @@
(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');
})();
+7
View File
@@ -0,0 +1,7 @@
class WRAboutViewController {
viewDidLoad() {
self.origin__viewDidLoad();
// 把原来的 rights 和 reserved 开头改成大写
self.copyrightLabel().setText('腾讯公司 版权所有\nCopyright \u00A9 1998 - 2022 Tencent. All Rights Reserved.');
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,711 @@
/**
* Highlighter module for Rangy, a cross-browser JavaScript range and selection library
* https://github.com/timdown/rangy
*
* Depends on Rangy core, ClassApplier and optionally TextRange modules.
*
* Copyright 2015, Tim Down
* Licensed under the MIT license.
* Version: 1.3.0
* Build date: 10 May 2015
*/
(function (factory, root) {
if (typeof define == "function" && define.amd) {
// AMD. Register as an anonymous module with a dependency on Rangy.
define(["./rangy-core"], factory);
} else if (typeof module != "undefined" && typeof exports == "object") {
// Node/CommonJS style
module.exports = factory(require("rangy"));
} else {
// No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
factory(root.rangy);
}
})(function (rangy) {
rangy.createModule("Highlighter", ["ClassApplier"], function (api, module) {
var dom = api.dom;
var contains = dom.arrayContains;
var getBody = dom.getBody;
var createOptions = api.util.createOptions;
var forEach = api.util.forEach;
var nextHighlightId = 1;
// Puts highlights in order, last in document first.
function compareHighlights(h1, h2) {
return h1.characterRange.start - h2.characterRange.start;
}
function getContainerElement(doc, id) {
return id ? doc.getElementById(id) : getBody(doc);
}
/*----------------------------------------------------------------------------------------------------------------*/
var highlighterTypes = {};
function HighlighterType(type, converterCreator) {
this.type = type;
this.converterCreator = converterCreator;
}
HighlighterType.prototype.create = function () {
var converter = this.converterCreator();
converter.type = this.type;
return converter;
};
function registerHighlighterType(type, converterCreator) {
highlighterTypes[type] = new HighlighterType(type, converterCreator);
}
function getConverter(type) {
var highlighterType = highlighterTypes[type];
if (highlighterType instanceof HighlighterType) {
return highlighterType.create();
} else {
throw new Error("Highlighter type '" + type + "' is not valid");
}
}
api.registerHighlighterType = registerHighlighterType;
/*----------------------------------------------------------------------------------------------------------------*/
function CharacterRange(start, end) {
this.start = start;
this.end = end;
}
CharacterRange.prototype = {
intersects: function (charRange) {
return this.start < charRange.end && this.end > charRange.start;
},
isContiguousWith: function (charRange) {
return this.start == charRange.end || this.end == charRange.start;
},
isEqual: function (charRange) {
return this.start === charRange.start && this.end === charRange.end
},
isContain: function (charRange, exclusive) {
return exclusive ? this.start < charRange.start && this.end > charRange.end
: this.start <= charRange.start && this.end >= charRange.end
},
union: function (charRange) {
return new CharacterRange(Math.min(this.start, charRange.start), Math.max(this.end, charRange.end));
},
intersection: function (charRange) {
return new CharacterRange(Math.max(this.start, charRange.start), Math.min(this.end, charRange.end));
},
getComplements: function (charRange) {
var ranges = [];
if (this.start >= charRange.start) {
if (this.end <= charRange.end) {
return [];
}
ranges.push(new CharacterRange(charRange.end, this.end));
} else {
ranges.push(new CharacterRange(this.start, Math.min(this.end, charRange.start)));
if (this.end > charRange.end) {
ranges.push(new CharacterRange(charRange.end, this.end));
}
}
return ranges;
},
toString: function () {
return "[CharacterRange(" + this.start + ", " + this.end + ")]";
}
};
CharacterRange.fromCharacterRange = function (charRange) {
return new CharacterRange(charRange.start, charRange.end);
};
/*----------------------------------------------------------------------------------------------------------------*/
var textContentConverter = {
rangeToCharacterRange: function (range, containerNode) {
var bookmark = range.getBookmark(containerNode);
return new CharacterRange(bookmark.start, bookmark.end);
},
characterRangeToRange: function (doc, characterRange, containerNode) {
var range = api.createRange(doc);
range.moveToBookmark({
start: characterRange.start,
end: characterRange.end,
containerNode: containerNode
});
return range;
},
serializeSelection: function (selection, containerNode) {
var ranges = selection.getAllRanges(), rangeCount = ranges.length;
var rangeInfos = [];
var backward = rangeCount == 1 && selection.isBackward();
for (var i = 0, len = ranges.length; i < len; ++i) {
rangeInfos[i] = {
characterRange: this.rangeToCharacterRange(ranges[i], containerNode),
backward: backward
};
}
return rangeInfos;
},
restoreSelection: function (selection, savedSelection, containerNode) {
selection.removeAllRanges();
var doc = selection.win.document;
for (var i = 0, len = savedSelection.length, range, rangeInfo, characterRange; i < len; ++i) {
rangeInfo = savedSelection[i];
characterRange = rangeInfo.characterRange;
range = this.characterRangeToRange(doc, rangeInfo.characterRange, containerNode);
selection.addRange(range, rangeInfo.backward);
}
}
};
registerHighlighterType("textContent", function () {
return textContentConverter;
});
/*----------------------------------------------------------------------------------------------------------------*/
// Lazily load the TextRange-based converter so that the dependency is only checked when required.
registerHighlighterType("TextRange", (function () {
var converter;
return function () {
if (!converter) {
// Test that textRangeModule exists and is supported
var textRangeModule = api.modules.TextRange;
if (!textRangeModule) {
throw new Error("TextRange module is missing.");
} else if (!textRangeModule.supported) {
throw new Error("TextRange module is present but not supported.");
}
converter = {
rangeToCharacterRange: function (range, containerNode) {
return CharacterRange.fromCharacterRange(range.toCharacterRange(containerNode));
},
characterRangeToRange: function (doc, characterRange, containerNode) {
var range = api.createRange(doc);
range.selectCharacters(containerNode, characterRange.start, characterRange.end);
return range;
},
serializeSelection: function (selection, containerNode) {
return selection.saveCharacterRanges(containerNode);
},
restoreSelection: function (selection, savedSelection, containerNode) {
selection.restoreCharacterRanges(containerNode, savedSelection);
}
};
}
return converter;
};
})());
/*----------------------------------------------------------------------------------------------------------------*/
function Highlight(doc, characterRange, classApplier, converter, id, containerElementId) {
if (id) {
this.id = id;
nextHighlightId = Math.max(nextHighlightId, id + 1);
} else {
this.id = nextHighlightId++;
}
this.characterRange = characterRange;
this.doc = doc;
this.classApplier = classApplier;
this.converter = converter;
this.containerElementId = containerElementId || null;
this.applied = false;
this.innerText = this.getText().replace(/\s+/g, '');
}
Highlight.prototype = {
getContainerElement: function () {
return getContainerElement(this.doc, this.containerElementId);
},
getRange: function () {
return this.converter.characterRangeToRange(this.doc, this.characterRange, this.getContainerElement());
},
fromRange: function (range) {
this.characterRange = this.converter.rangeToCharacterRange(range, this.getContainerElement());
},
getText: function () {
return this.getRange().toString();
},
containsElement: function (el) {
return this.getRange().containsNodeContents(el.firstChild);
},
unapply: function () {
this.classApplier.undoToRange(this.getRange());
this.applied = false;
},
apply: function () {
this.classApplier.applyToRange(this.getRange());
this.applied = true;
},
getHighlightElements: function () {
return this.classApplier.getElementsWithClassIntersectingRange(this.getRange());
},
toString: function () {
return "[Highlight(ID: " + this.id + ", class: " + this.classApplier.className + ", character range: " +
this.characterRange.start + " - " + this.characterRange.end + ")]";
}
};
/*----------------------------------------------------------------------------------------------------------------*/
function Highlighter(doc, type) {
type = type || "textContent";
this.doc = doc || document;
this.classAppliers = {};
this.highlights = [];
this.converter = getConverter(type);
}
Highlighter.prototype = {
addClassApplier: function (classApplier) {
this.classAppliers[classApplier.className] = classApplier;
},
getMergedCharacterRange: function (characterRange, classNames, isSure) {
var highlights = this.highlights, i, len = highlights.length, hRange
var useHighlights =[]
for (i = 0; i < len; i++) {
if (classNames.indexOf(highlights[i].classApplier.className) >= 0) {
useHighlights.push(highlights[i])
}
}
len = useHighlights.length
if(!len){
return null
}
if(!isSure){
for (i = 0; i < len; i++) {
hRange = useHighlights[i].characterRange
if(hRange.isContain(characterRange)){
characterRange = hRange
isSure = true
break
}
}
if(!isSure){
return null
}
}
for (i = 0; i < len; i++) {
hRange = useHighlights[i].characterRange
if(hRange.intersects(characterRange) || hRange.isContiguousWith(characterRange)){
characterRange = hRange.union(characterRange)
}
}
return characterRange;
},
createCharacterRange: function (start, end) {
return new CharacterRange(start, end)
},
getHighlightForElement: function (el) {
var highlights = this.highlights;
var possibleMatches = [];
var possibleMatchesContainReview = false;
for (var i = 0, len = highlights.length; i < len; ++i) {
if (highlights[i].containsElement(el)) {
possibleMatches.push(highlights[i]);
if (highlights[i].classApplier.className === 'review') {
possibleMatchesContainReview = true;
}
}
}
if (!possibleMatches.length) {
return null;
}
if (!possibleMatchesContainReview) {
return possibleMatches[0];
}
for (var i = 0, len = possibleMatches.length; i < len; ++i) {
if (possibleMatches[i].classApplier.className === 'review') {
return possibleMatches[i];
}
}
return null;
},
removeHighlights: function (highlights) {
for (var i = 0, len = this.highlights.length, highlight; i < len; ++i) {
highlight = this.highlights[i];
if (contains(highlights, highlight)) {
highlight.unapply();
this.highlights.splice(i--, 1);
}
}
},
removeAllHighlights: function () {
this.removeHighlights(this.highlights);
},
getIntersectingHighlights: function (ranges) {
// Test each range against each of the highlighted ranges to see whether they overlap
var intersectingHighlights = [], highlights = this.highlights;
forEach(ranges, function (range) {
//var selCharRange = converter.rangeToCharacterRange(range);
forEach(highlights, function (highlight) {
if (range.intersectsRange(highlight.getRange()) && !contains(intersectingHighlights, highlight)) {
intersectingHighlights.push(highlight);
}
});
});
return intersectingHighlights;
},
highghtWhichContainSelection: function (classNames, selection) {
selection = selection || api.getSelection(this.doc)
var highlights = this.highlights, ranges = selection.getAllRanges()
var highlight, range
loop: for(var i =0; i < highlights.length; i++){
highlight = highlights[i]
if(classNames.indexOf(highlight.classApplier.className) < 0){
continue
}
for(var j=0; j< ranges.length; j++){
range = ranges[j]
if(!highlight.getRange().containsRange(range)){
continue loop
}
}
return highlight
}
return null
},
highlightCharacterRanges: function (className, charRanges, options) {
var i, len, j;
var highlights = this.highlights;
var converter = this.converter;
var doc = this.doc;
var highlightsToRemove = [];
var classApplier = className ? this.classAppliers[className] : null;
options = createOptions(options, {
containerElementId: null,
exclusive: true
});
var containerElementId = options.containerElementId;
var exclusive = options.exclusive;
var containerElement, containerElementRange, containerElementCharRange;
if (containerElementId) {
containerElement = this.doc.getElementById(containerElementId);
if (containerElement) {
containerElementRange = api.createRange(this.doc);
containerElementRange.selectNodeContents(containerElement);
containerElementCharRange = new CharacterRange(0, containerElementRange.toString().length);
}
}
var charRange, highlightCharRange, removeHighlight, isSameClassApplier, highlightsToKeep,
splitHighlight;
for (i = 0, len = charRanges.length; i < len; ++i) {
charRange = charRanges[i];
highlightsToKeep = [];
// Restrict character range to container element, if it exists
if (containerElementCharRange) {
charRange = charRange.intersection(containerElementCharRange);
}
// Ignore empty ranges
if (charRange.start === charRange.end) {
continue;
}
// Check for intersection with existing highlights. For each intersection, create a new highlight
// which is the union of the highlight range and the selected range
for (j = 0; j < highlights.length; ++j) {
removeHighlight = false;
if (containerElementId === highlights[j].containerElementId) {
highlightCharRange = highlights[j].characterRange;
isSameClassApplier = (classApplier === highlights[j].classApplier);
splitHighlight = !isSameClassApplier && exclusive;
// Replace the existing highlight if it needs to be:
// 1. merged (isSameClassApplier)
// 2. partially or entirely erased (className === null)
// 3. partially or entirely replaced (isSameClassApplier == false && exclusive == true)
if ((highlightCharRange.intersects(charRange) || highlightCharRange.isContiguousWith(charRange)) &&
(isSameClassApplier || splitHighlight)) {
// Remove existing highlights, keeping the unselected parts
if (splitHighlight) {
forEach(highlightCharRange.getComplements(charRange), function (rangeToAdd) {
highlightsToKeep.push(new Highlight(doc, rangeToAdd, highlights[j].classApplier, converter, null, containerElementId));
});
}
removeHighlight = true;
if (isSameClassApplier) {
charRange = highlightCharRange.union(charRange);
}
}
}
if (removeHighlight) {
highlightsToRemove.push(highlights[j]);
highlights[j] = new Highlight(doc, highlightCharRange.union(charRange), classApplier, converter, null, containerElementId);
}else{
highlightsToKeep.push(highlights[j]);
}
}
// Add new range
if (classApplier) {
highlightsToKeep.push(new Highlight(doc, charRange, classApplier, converter, null, containerElementId));
}
this.highlights = highlights = highlightsToKeep;
}
// Remove the old highlights
forEach(highlightsToRemove, function (highlightToRemove) {
highlightToRemove.unapply();
});
// Apply new highlights
var newHighlights = [];
forEach(highlights, function (highlight) {
if (!highlight.applied) {
highlight.apply();
// 只返回 class 相同的 highlight, 过滤掉因 split 而产生的新的 highlight
if(highlight.classApplier === classApplier){
newHighlights.push(highlight);
}
}
});
return newHighlights;
},
highlightRanges: function (className, ranges, options) {
var selCharRanges = [];
var converter = this.converter;
options = createOptions(options, {
containerElement: null,
exclusive: true
});
var containerElement = options.containerElement;
var containerElementId = containerElement ? containerElement.id : null;
var containerElementRange;
if (containerElement) {
containerElementRange = api.createRange(containerElement);
containerElementRange.selectNodeContents(containerElement);
}
forEach(ranges, function (range) {
var scopedRange = containerElement ? containerElementRange.intersection(range) : range;
selCharRanges.push(converter.rangeToCharacterRange(scopedRange, containerElement || getBody(range.getDocument())));
});
return this.highlightCharacterRanges(className, selCharRanges, {
containerElementId: containerElementId,
exclusive: options.exclusive
});
},
highlightSelection: function (className, options) {
var converter = this.converter;
var classApplier = className ? this.classAppliers[className] : false;
options = createOptions(options, {
containerElementId: null,
selection: api.getSelection(this.doc),
exclusive: true
});
var containerElementId = options.containerElementId;
var exclusive = options.exclusive;
var selection = options.selection;
var doc = selection.win.document;
var containerElement = getContainerElement(doc, containerElementId);
if (!classApplier && className !== false) {
throw new Error("No class applier found for class '" + className + "'");
}
// Store the existing selection as character ranges
var serializedSelection = converter.serializeSelection(selection, containerElement);
// Create an array of selected character ranges
var selCharRanges = [];
forEach(serializedSelection, function (rangeInfo) {
selCharRanges.push(CharacterRange.fromCharacterRange(rangeInfo.characterRange));
});
var newHighlights = this.highlightCharacterRanges(className, selCharRanges, {
containerElementId: containerElementId,
exclusive: exclusive
});
// Restore selection
converter.restoreSelection(selection, serializedSelection, containerElement);
return newHighlights;
},
unhighlightSelection: function (selection) {
selection = selection || api.getSelection(this.doc);
var intersectingHighlights = this.getIntersectingHighlights(selection.getAllRanges());
this.removeHighlights(intersectingHighlights);
selection.removeAllRanges();
return intersectingHighlights;
},
getHighlightsInSelection: function (selection) {
selection = selection || api.getSelection(this.doc);
return this.getIntersectingHighlights(selection.getAllRanges());
},
selectionOverlapsHighlight: function (selection) {
return this.getHighlightsInSelection(selection).length > 0;
},
serialize: function (options) {
var highlighter = this;
var highlights = highlighter.highlights;
var serializedType, serializedHighlights, convertType, serializationConverter;
highlights.sort(compareHighlights);
options = createOptions(options, {
serializeHighlightText: false,
type: highlighter.converter.type
});
serializedType = options.type;
convertType = (serializedType != highlighter.converter.type);
if (convertType) {
serializationConverter = getConverter(serializedType);
}
serializedHighlights = ["type:" + serializedType];
forEach(highlights, function (highlight) {
var characterRange = highlight.characterRange;
var containerElement;
// Convert to the current Highlighter's type, if different from the serialization type
if (convertType) {
containerElement = highlight.getContainerElement();
characterRange = serializationConverter.rangeToCharacterRange(
highlighter.converter.characterRangeToRange(highlighter.doc, characterRange, containerElement),
containerElement
);
}
var parts = [
characterRange.start,
characterRange.end,
highlight.id,
highlight.classApplier.className,
highlight.containerElementId
];
if (options.serializeHighlightText) {
parts.push(highlight.getText());
}
serializedHighlights.push(parts.join("$"));
});
return serializedHighlights.join("|");
},
deserialize: function (serialized) {
var serializedHighlights = serialized.split("|");
var highlights = [];
var firstHighlight = serializedHighlights[0];
var regexResult;
var serializationType, serializationConverter, convertType = false;
if (firstHighlight && (regexResult = /^type:(\w+)$/.exec(firstHighlight))) {
serializationType = regexResult[1];
if (serializationType != this.converter.type) {
serializationConverter = getConverter(serializationType);
convertType = true;
}
serializedHighlights.shift();
} else {
throw new Error("Serialized highlights are invalid.");
}
var classApplier, highlight, characterRange, containerElementId, containerElement;
for (var i = serializedHighlights.length, parts; i-- > 0;) {
parts = serializedHighlights[i].split("$");
characterRange = new CharacterRange(+parts[0], +parts[1]);
containerElementId = parts[4] || null;
// Convert to the current Highlighter's type, if different from the serialization type
if (convertType) {
containerElement = getContainerElement(this.doc, containerElementId);
characterRange = this.converter.rangeToCharacterRange(
serializationConverter.characterRangeToRange(this.doc, characterRange, containerElement),
containerElement
);
}
classApplier = this.classAppliers[parts[3]];
if (!classApplier) {
throw new Error("No class applier found for class '" + parts[3] + "'");
}
highlight = new Highlight(this.doc, characterRange, classApplier, this.converter, parseInt(parts[2]), containerElementId);
highlight.apply()
highlights.push(highlight);
}
this.highlights = highlights;
}
};
api.Highlighter = Highlighter;
api.createHighlighter = function (doc, rangeCharacterOffsetConverterType) {
return new Highlighter(doc, rangeCharacterOffsetConverterType);
};
});
return rangy;
}, this);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,16 @@
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';
}
}
}
}
File diff suppressed because it is too large Load Diff