Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

增加 一键转换 LaTeX 公式 功能 #6

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 142 additions & 1 deletion mpMath/assets/js/content-script.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ function fixClick(event) {
event.stopPropagation();
}

function convertClick(event) {
processFormula();
$('.tpl_dropdown_menu', '.formula').css('display', 'none');
event.stopPropagation();
}

function guideClick(event) {
alert('指南还在施工!');
$('.tpl_dropdown_menu', '.formula').css('display', 'none');
Expand Down Expand Up @@ -69,6 +75,12 @@ chrome.runtime.sendMessage({}, function (response) {
formulaFixItem.onclick = fixClick;
dropdownMenu.appendChild(formulaFixItem);

let convertFormulaItem = document.createElement('li');
convertFormulaItem.setAttribute('class', 'tpl_dropdown_menu_item');
convertFormulaItem.innerText = '转化公式';
convertFormulaItem.onclick = convertClick;
dropdownMenu.appendChild(convertFormulaItem);

let formulaGuide = document.createElement('li');
formulaGuide.setAttribute('class', 'tpl_dropdown_menu_item');
formulaGuide.innerText = '指南';
Expand Down Expand Up @@ -148,4 +160,133 @@ function revise() {
//alert('Revise complete!');
alert(`修复了 ${embeds.length} 个目标!`);
})
}
}


// 返回 container 第一个公式 range, inline .
// range 指定了公式范围, inline = true 则意味着公式是 inline 的
function findFormula(container) {
var nodeIterator = document.createNodeIterator(container, NodeFilter.SHOW_TEXT);

var currentNode;
// 公式可能会跨越多个 node.
var startNode = null;
var startOffset = -1;
var inline = false;
while ((currentNode = nodeIterator.nextNode())) {
var searchStart = 0;
while (searchStart < currentNode.nodeValue.length) {
if (startNode == null) {
var dollarIndex = currentNode.nodeValue.indexOf('$', searchStart);
if (dollarIndex == -1) {
searchStart = currentNode.nodeValue.length;
continue;
}
if (dollarIndex > 0 && currentNode.nodeValue.charAt(dollarIndex - 1) == '\\') {
// \$ 表示对 $ 的转义, 此时 $ 并不是表示公式的开始.
searchStart = dollarIndex + 1;
continue;
}
startNode = currentNode
startOffset = dollarIndex;
if (dollarIndex + 1 < currentNode.nodeValue.length && currentNode.nodeValue.charAt(dollarIndex + 1) == '$') {
// 行间公式
inline = false;
searchStart = dollarIndex + 2;
} else {
inline = true;
searchStart = dollarIndex + 1;
}
continue;
}
var dollarIndex = currentNode.nodeValue.indexOf('$', searchStart);
if (dollarIndex == -1) {
searchStart = currentNode.nodeValue.length;
continue;
}
if (dollarIndex > 0 && currentNode.nodeValue.charAt(dollarIndex - 1) == '\\') {
// \$ 表示对 $ 的转义, 此时 $ 并不是表示公式的结束.
searchStart = dollarIndex + 1;
continue;
}
var endNode = currentNode
var endOffset = dollarIndex;
if (dollarIndex + 1 < currentNode.nodeValue.length && currentNode.nodeValue.charAt(dollarIndex + 1) == '$') {
endOffset = dollarIndex + 1;
searchStart = dollarIndex + 2;
} else {
searchStart = dollarIndex + 1;
}
var range = document.createRange();
range.setStart(startNode, startOffset);
range.setEnd(endNode, endOffset + 1); // + 1 是开区间.
return {
range: range,
inline: inline
};
}
};
return null;
}

function getFormulaTxt(formula) {
var text = formula.range.toString();
if (formula.inline) {
return text.substring(1, text.length - 1);
} else {
return text.substring(2, text.length - 2);
}
}

function mergeNode(firstP, secondP) {
for (var node of secondP.childNodes) {
firstP.appendChild(node);
}
secondP.remove();
}

// 定义一个递归函数来处理每个公式
function processFormula() {
var body = $('#ueditor_0').contents().find('.view')[0];
var formula = findFormula(body);
if (!formula) {
// 没有更多的公式需要处理
return Promise.resolve(); // 返回一个解决的Promise
}

var latexText = getFormulaTxt(formula).trim();
console.log("转化", latexText);

// 发送消息到扩展,并等待响应
return chrome.runtime.sendMessage({
action: 'convert',
input: latexText,
display: !formula.inline
}).then(response => {
// 收到响应后,使用响应结果
let parser = new DOMParser();
let doc = parser.parseFromString(response.result, 'text/html');
let outputNode = doc.body.firstChild;

if (formula.inline && formula.range.startContainer != formula.range.endContainer) {
// 我们在这里处理如下情况, 即 `$` 跨越了多个元素.
// <p>明显 $</p>
// <p>|I| = |I_1| + |I_2| + |I_3|</p>
// <p>$. <svg></svg></p>
// 如果不做任何处理, 则输出结果丑了一点.
let startTextNode = formula.range.startContainer; // 一定是 text.
let endTextNode = formula.range.endContainer; // 这也是个 text node.
let startNode = startTextNode.parentNode;
let endNode = endTextNode.parentNode;
formula.range.deleteContents();
startNode.appendChild(outputNode);
mergeNode(startNode, endNode);
} else {
formula.range.deleteContents();
formula.range.insertNode(outputNode);
}

// 递归处理下一个公式
return processFormula();
});
}
57 changes: 42 additions & 15 deletions mpMath/assets/js/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,35 @@ function checkNull(str) {
}
}


function formula2Svg(latexText, display) {
var outputNode = document.getElementById('output');
outputNode.innerHTML = '';
MathJax.texReset();
let options = MathJax.getMetricsFor(outputNode);
options.display = display;
var latxnode = MathJax.tex2svg(latexText, options);
MathJax.startup.document.clear();
MathJax.startup.document.updateDocument();
return latxnode;
}

// 将生成的mjx-container套在span中
function svg2outHTML(latexNode, latexText, display) {
let sp = document.createElement('span');
if (display) {
latexNode.style = 'overflow-x:auto; outline:0; display:block; text-align: center; margin: 15px 0px;';
latexNode.setAttribute('display', true);
latexNode.childNodes[0].style = 'height:auto; max-width:300% !important;'
}
//latexNode.setAttribute('data-formula', input.value.trim().replace(/\\/g, '\\\\'));
latexNode.setAttribute('data-formula', latexText);
sp.setAttribute('style', 'cursor:pointer;');
sp.appendChild(latexNode);
sp.innerHTML = sp.innerHTML.replace(/<mjx-assistive-mml.+?<\/mjx-assistive-mml>/g, "");
return sp.outerHTML;
}

// Tex代码转SVG图像
function convert() {
let inputTex = document.getElementById("input").value.trim();
Expand Down Expand Up @@ -49,22 +78,9 @@ function closeFrame() {
function insertFormula() {
if (insert.disabled == true) return;

// 将生成的mjx-container套在span中
let output = document.getElementById('output');
let sp = document.createElement('span');
if ($(block).prop('checked')) {
output.childNodes[0].style = 'overflow-x:auto; outline:0; display:block; text-align: center; margin: 15px 0px;'
output.childNodes[0].setAttribute('display', true);
output.childNodes[0].childNodes[0].style = 'height:auto; max-width:300% !important;'
}

//output.childNodes[0].setAttribute('data-formula', input.value.trim().replace(/\\/g, '\\\\'));
output.childNodes[0].setAttribute('data-formula', input.value.trim());
sp.setAttribute('style', 'cursor:pointer;');
sp.appendChild(output.childNodes[0]);
sp.innerHTML = sp.innerHTML.replace(/<mjx-assistive-mml.+?<\/mjx-assistive-mml>/g, "");
var outerHTML = svg2outHTML(output.childNodes[0], input.value.trim(), $(block).prop('checked'));

parent.window.postMessage({ type: 'INSERT_FORMULA', text: sp.outerHTML }, '*');
parent.window.postMessage({ type: 'INSERT_FORMULA', text: outerHTML }, '*');
input.value = '';
closeFrame();
}
Expand Down Expand Up @@ -92,6 +108,17 @@ $(function() {
}
});


chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (message.action === 'convert') {
console.log("receiver convert message", message);
var result = formula2Svg(message.input, message.display);
var outerHTML = svg2outHTML(result, message.input, message.display);
sendResponse({result: outerHTML});
}
});


// 防止窗口失去焦点
$(window).focusout(function() {
setTimeout(function() {
Expand Down