init
This commit is contained in:
181
src/App.css
Executable file
181
src/App.css
Executable file
@ -0,0 +1,181 @@
|
||||
.App {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* apply a natural box layout model to all elements */
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
font-family: 'Anaheim', sans-serif;
|
||||
}
|
||||
|
||||
#output,
|
||||
#preview,
|
||||
#input {
|
||||
width: 100%;
|
||||
display: block;
|
||||
height: 40%;
|
||||
border: none;
|
||||
font-family: 'Anaheim', sans-serif;
|
||||
}
|
||||
|
||||
#input {
|
||||
height: 10%;
|
||||
background-color: #7a9a95;
|
||||
-webkit-transition: background-color 250ms;
|
||||
-moz-transition: background-color 250ms;
|
||||
transition: background-color 250ms;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#output {
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
|
||||
#input:focus {
|
||||
background-color: #90c5a9;
|
||||
}
|
||||
|
||||
#preview {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.Power {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
padding: 16px 10px 10px 10px;
|
||||
background-color: aliceblue;
|
||||
}
|
||||
|
||||
.PowerType {
|
||||
max-width: 320px;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#loading {
|
||||
background-color: #45b29d;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
z-index: 99999999999;
|
||||
margin-top: 0px;
|
||||
top: 0px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.loading-title {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#loading-center {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#loading-center-absolute {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
height: 50px;
|
||||
width: 150px;
|
||||
margin-top: -25px;
|
||||
margin-left: -75px;
|
||||
|
||||
}
|
||||
|
||||
.object {
|
||||
width: 8px;
|
||||
height: 50px;
|
||||
margin-right: 5px;
|
||||
background-color: #FFF;
|
||||
-webkit-animation: animate 1s infinite;
|
||||
animation: animate 1s infinite;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.object:last-child {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.object:nth-child(10) {
|
||||
-webkit-animation-delay: 0.9s;
|
||||
animation-delay: 0.9s;
|
||||
}
|
||||
|
||||
.object:nth-child(9) {
|
||||
-webkit-animation-delay: 0.8s;
|
||||
animation-delay: 0.8s;
|
||||
}
|
||||
|
||||
.object:nth-child(8) {
|
||||
-webkit-animation-delay: 0.7s;
|
||||
animation-delay: 0.7s;
|
||||
}
|
||||
|
||||
.object:nth-child(7) {
|
||||
-webkit-animation-delay: 0.6s;
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.object:nth-child(6) {
|
||||
-webkit-animation-delay: 0.5s;
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
.object:nth-child(5) {
|
||||
-webkit-animation-delay: 0.4s;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.object:nth-child(4) {
|
||||
-webkit-animation-delay: 0.3s;
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.object:nth-child(3) {
|
||||
-webkit-animation-delay: 0.2s;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.object:nth-child(2) {
|
||||
-webkit-animation-delay: 0.1s;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes animate {
|
||||
50% {
|
||||
-ms-transform: scaleY(0);
|
||||
-webkit-transform: scaleY(0);
|
||||
transform: scaleY(0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animate {
|
||||
50% {
|
||||
-ms-transform: scaleY(0);
|
||||
-webkit-transform: scaleY(0);
|
||||
transform: scaleY(0);
|
||||
}
|
||||
}
|
444
src/App.js
Executable file
444
src/App.js
Executable file
@ -0,0 +1,444 @@
|
||||
import React, { Component } from 'react';
|
||||
import './App.css';
|
||||
import Axios from 'axios';
|
||||
const removeUnused = require('postcss-remove-unused');
|
||||
const postcss = require('postcss');
|
||||
const prettier = require("prettier/standalone");
|
||||
const plugins = [require("prettier/parser-html"), require("prettier/parser-postcss")];
|
||||
const CSS = require('css')
|
||||
const cssjs = require("jotform-css.js");
|
||||
//initialize parser object
|
||||
const parser = new cssjs.cssjs();
|
||||
|
||||
// 移除指定标签
|
||||
function removeTags(tagName, el = document){
|
||||
var tagElements = el.getElementsByTagName(tagName);
|
||||
for(var m = tagElements.length - 1; m >= 0; m--){
|
||||
tagElements[m].parentNode.removeChild( tagElements[m]);
|
||||
}
|
||||
}
|
||||
|
||||
// 移除所有CSS
|
||||
function removeAllCSS (el = document) {
|
||||
el.querySelectorAll('link').forEach(linkEl => linkEl.rel === 'stylesheet' && linkEl.remove());
|
||||
el.querySelectorAll('style').forEach(styleEl => styleEl.remove());
|
||||
(function recurse (node) {
|
||||
node.childNodes.forEach(recurse);
|
||||
if (node.removeAttribute) {
|
||||
node.removeAttribute('height')
|
||||
node.removeAttribute('border')
|
||||
node.removeAttribute('cellpadding')
|
||||
node.removeAttribute('cellspacing')
|
||||
node.removeAttribute('class')
|
||||
}
|
||||
return node.removeAttribute && node.removeAttribute('style');
|
||||
})(el.body);
|
||||
}
|
||||
|
||||
// 移除表格所有宽度
|
||||
function removeTableWidth (el = document) {
|
||||
el.querySelectorAll('width').forEach(styleEl => styleEl.remove());
|
||||
(function recurse (node) {
|
||||
node.childNodes.forEach(recurse);
|
||||
return node.removeAttribute && node.removeAttribute('width');
|
||||
})(el.body);
|
||||
}
|
||||
|
||||
// 为CSS的AST添加前缀
|
||||
function scopeCSS(css, prefix) {
|
||||
var ast = CSS.parse(css);
|
||||
var stylesheet = ast.stylesheet;
|
||||
if (stylesheet) {
|
||||
var rules = stylesheet.rules;
|
||||
// Append our container scope to rules
|
||||
// Recursive rule appender
|
||||
var ruleAppend = function(rules) {
|
||||
rules.forEach(function(rule) {
|
||||
if (rule.selectors !== undefined) {
|
||||
rule.selectors = rule.selectors.map(function(selector) {
|
||||
return prefix + " " + selector;
|
||||
});
|
||||
}
|
||||
if (rule.rules !== undefined) {
|
||||
ruleAppend(rule.rules);
|
||||
}
|
||||
});
|
||||
};
|
||||
ruleAppend(rules);
|
||||
}
|
||||
return CSS.stringify(ast);
|
||||
}
|
||||
|
||||
// 移除指定的CSS样式
|
||||
function removeCSS(ast, names) {
|
||||
ast = ast.map(function(rule) {
|
||||
if (rule.selector) {
|
||||
for (const n in names) {
|
||||
if (rule.selector === names[n]) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
return rule
|
||||
});
|
||||
ast = ast.filter(function (s) {
|
||||
return s != null
|
||||
});
|
||||
return ast;
|
||||
}
|
||||
|
||||
// 移除包含指定字符串的CSS样式
|
||||
function removeCSSIncludes(ast, names) {
|
||||
ast = ast.map(function(rule) {
|
||||
if (rule.selector) {
|
||||
for (const n in names) {
|
||||
if (rule.selector.includes(names[n])) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
return rule
|
||||
});
|
||||
ast = ast.filter(function (s) {
|
||||
return s != null
|
||||
});
|
||||
return ast;
|
||||
}
|
||||
|
||||
// 移除包含指定字符串的CSS属性
|
||||
function removeCSSatt(ast, names) {
|
||||
ast = ast.map(function(rule) {
|
||||
let rules = rule.rules
|
||||
rules = rules.map((r) => {
|
||||
if (r.directive) {
|
||||
for (const n in names) {
|
||||
if (r.directive.includes(names[n])) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
if (r.value && r.value.length - 2 > 0 && r.value.lastIndexOf('pt') === r.value.length - 2) { // 判断以 pt 结尾
|
||||
r.value = r.value.replaceAll('0cm', '0').replaceAll('pt', 'px')
|
||||
}
|
||||
return r
|
||||
})
|
||||
|
||||
rules = rules.filter(function (s) {
|
||||
return s != null
|
||||
});
|
||||
|
||||
rule.rules = rules
|
||||
return rule
|
||||
});
|
||||
ast = ast.filter(function (s) {
|
||||
return s != null
|
||||
});
|
||||
return ast;
|
||||
}
|
||||
|
||||
// 生成随机字符串
|
||||
function makeid() {
|
||||
var text = "";
|
||||
var possible = "abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
for( var i=0; i < 8; i++ )
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
// 表格宽度转百分比
|
||||
function convertWidthToPercent (el = document) {
|
||||
const tables = el.body.getElementsByTagName('table')
|
||||
for (const n in tables) {
|
||||
const table = tables[n]
|
||||
if (table) {
|
||||
const tableWidth = table.offsetWidth;
|
||||
if (table.removeAttribute) {
|
||||
table.removeAttribute('width')
|
||||
}
|
||||
|
||||
if (table.style && table.style.width) table.style.width = '100%'
|
||||
|
||||
const tds = el.body.getElementsByTagName('td')
|
||||
for (const i in tds) {
|
||||
const node = tds[i]
|
||||
if (node.removeAttribute) {
|
||||
node.removeAttribute('width')
|
||||
}
|
||||
if (node.offsetWidth && node.offsetWidth > 0) {
|
||||
node.style.width = (node.offsetWidth / tableWidth).toFixed(3) * 100 + '%'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 移除Style中Office字体样式和专用样式
|
||||
function removeStyleatt (el = document) {
|
||||
const tables = el.body.getElementsByTagName('table');
|
||||
(function recurse (nodes) {
|
||||
for (const i in nodes) {
|
||||
const node = nodes[i]
|
||||
if (node.getAttribute) {
|
||||
const style = node.getAttribute('style')
|
||||
if (style) {
|
||||
const classid = makeid()
|
||||
let parsed = parser.parseCSS('.' + classid + '{' + style + '}');
|
||||
parsed = removeCSSatt(parsed, ['font-family', 'mso-', 'page-break-inside'])
|
||||
let newCSSString = parser.getCSSForEditor(parsed);
|
||||
newCSSString = newCSSString.match(/\{[\S\s]+\}/)
|
||||
if (newCSSString && newCSSString.length >= 1) {
|
||||
newCSSString = newCSSString[0].substring(1, newCSSString[0].length-1).replace(/\s/g,'').replace(/<\/?.+?>/g,"").replace(/[\r\n]/g, "")
|
||||
if (newCSSString !== '') {
|
||||
node.setAttribute('style', newCSSString)
|
||||
}
|
||||
} else {
|
||||
node.removeAttribute('style')
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node.childNodes) recurse(node.childNodes)
|
||||
}
|
||||
})(tables);
|
||||
for (const x in tables) {
|
||||
if (tables[x] && tables[x].getAttribute) {
|
||||
let style = tables[x].getAttribute('style')
|
||||
if (style && style.includes('border:none;')) {
|
||||
style = style.replaceAll('border:none;', '')
|
||||
if (style !== '') {
|
||||
tables[x].setAttribute('style', style)
|
||||
} else {
|
||||
tables[x].removeAttribute('style')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
|
||||
constructor(){
|
||||
super();
|
||||
this.state = {
|
||||
value: '点击这里开始',
|
||||
html: '',
|
||||
type: 'excel',
|
||||
mobile: true,
|
||||
uncss: true,
|
||||
removeOfficeCss: true,
|
||||
compress: false,
|
||||
removeAllCSS: false,
|
||||
removeTableWidth: false,
|
||||
percent: false
|
||||
};
|
||||
}
|
||||
|
||||
textareaClick = (e) => {
|
||||
this.textareaInput.focus();
|
||||
this.textareaInput.select();
|
||||
}
|
||||
|
||||
inputFocus = (e) => {
|
||||
this.setState(() => ({value: '现在请使用 CTRL+V 粘贴Excel表格(计算量大,请耐心等待结果)'}));
|
||||
this.textInput.style.backgroundColor="#90c5a9";
|
||||
}
|
||||
|
||||
inputBlur = (e) => {
|
||||
this.setState(() => ({value: '点击这里开始'}));
|
||||
}
|
||||
|
||||
inputPaste = (e) => {
|
||||
document.getElementById("loading").style.display = 'block'
|
||||
// regexp
|
||||
var toReg = e.clipboardData.getData('text/html');
|
||||
setTimeout(async () => {
|
||||
let preid = makeid()
|
||||
var preview = document.getElementById("preview")
|
||||
var p = (preview.contentDocument || preview.contentWindow);
|
||||
p = p.document || p;
|
||||
|
||||
// console.log(toReg);
|
||||
toReg = toReg.replace(/(\r\n|\n|\r)/gm,"");
|
||||
var regstyle = /<STYLE*>.*<\/STYLE>/gi;
|
||||
var reg = /<TABLE.*>.*<\/TABLE>/gi;
|
||||
|
||||
let styleCode = toReg.match(regstyle)
|
||||
let tableCode = toReg.match(reg)
|
||||
|
||||
try {
|
||||
// 移除问题标签
|
||||
// console.log(styleCode)
|
||||
if (styleCode !== null) styleCode = styleCode.toString().replaceAll('<!--table', 'table').replaceAll('}--></style>', '}</style>').replaceAll('<!-- /\\* Font Definitions \\*/', '').replaceAll('<!--\\[if gte mso 10\\]>', '');
|
||||
// console.log(styleCode)
|
||||
if (tableCode !== null) tableCode = tableCode.toString().replaceAll('<!--StartFragment-->', '').replaceAll('<!--EndFragment-->', '').replaceAll('<!--\\[endif\\]-->', '');
|
||||
if (styleCode !== null) {
|
||||
styleCode = styleCode.replaceAll('<style>', '').replaceAll('</style>', '');
|
||||
|
||||
var CleanCSS = require('clean-css');
|
||||
var output = new CleanCSS({
|
||||
compatibility: 'ie9,-properties.merging'
|
||||
}).minify(styleCode);
|
||||
styleCode = output.styles
|
||||
|
||||
// 避免CSS污染全局样式
|
||||
// parse css string
|
||||
let parsed = parser.parseCSS(styleCode);
|
||||
parsed = removeCSS(parsed, ['html', 'body'])
|
||||
parsed = removeCSSIncludes(parsed, ['@'])
|
||||
|
||||
// 移除Office字体样式和专用样式
|
||||
if (this.state.removeOfficeCss) {
|
||||
parsed = removeCSSatt(parsed, ['mso-', 'font-family'])
|
||||
}
|
||||
|
||||
let newCSSString = parser.getCSSForEditor(parsed);
|
||||
// console.log(newCSSString)
|
||||
newCSSString = scopeCSS(newCSSString, '.' + preid)
|
||||
// console.log(newCSSString)
|
||||
|
||||
styleCode = newCSSString
|
||||
|
||||
let style = ''
|
||||
if (this.state.mobile) {
|
||||
style = `style="width: 100%;height: 80%;overflow: auto;"`
|
||||
}
|
||||
|
||||
tableCode = `<div class="${preid}" ${style}>` + tableCode + '</div>'
|
||||
|
||||
styleCode = prettier.format(styleCode, { parser: "css", plugins })
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
var finalCode;
|
||||
if(styleCode != null) {
|
||||
|
||||
if (this.state.uncss) {
|
||||
const cssResult = await postcss([ // 移除未使用的CSS
|
||||
removeUnused({html: tableCode})
|
||||
]).process(styleCode);
|
||||
|
||||
styleCode = cssResult.css
|
||||
}
|
||||
|
||||
finalCode = '<style>\n' + styleCode + '</style>\n\n' + tableCode;
|
||||
} else {
|
||||
finalCode = tableCode;
|
||||
}
|
||||
|
||||
if(finalCode != null){
|
||||
// iframe
|
||||
p.body.innerHTML = finalCode;
|
||||
|
||||
// 移除多余标签
|
||||
removeTags('col', p)
|
||||
removeTags('colgroup', p)
|
||||
|
||||
if (this.state.removeAllCSS) removeAllCSS(p)
|
||||
if (this.state.removeTableWidth) removeTableWidth(p)
|
||||
|
||||
// 转百分数
|
||||
if (this.state.percent) convertWidthToPercent(p)
|
||||
|
||||
// 移除Office字体样式和专用样式
|
||||
if (this.state.removeOfficeCss) {
|
||||
removeStyleatt(p)
|
||||
}
|
||||
|
||||
finalCode = p.body.innerHTML
|
||||
finalCode = prettier.format(finalCode, { parser: "html", plugins });
|
||||
|
||||
// 压缩HTML
|
||||
if (this.state.compress) {
|
||||
const {
|
||||
data
|
||||
} = await Axios.post('https://minifier.yige.ink', {
|
||||
code: finalCode
|
||||
})
|
||||
|
||||
if (data.status === 1) {
|
||||
finalCode = data.result
|
||||
}
|
||||
}
|
||||
|
||||
this.setState(() => ({html: finalCode}));
|
||||
this.textInput.style.backgroundColor="#7a9a95";
|
||||
this.textareaInput.focus();
|
||||
this.textareaInput.select();
|
||||
this.textareaInput.style.textAlign="left";
|
||||
this.setState(() => ({value: '现在使用 CTRL+C 可以复制下面的代码,或者点击这里开始粘贴其它表格。'}));
|
||||
this.textInput.style.backgroundColor="#fa8b60";
|
||||
} else {
|
||||
this.textInput.style.backgroundColor="#7a9a95";
|
||||
this.textareaInput.focus();
|
||||
this.setState(() => ({html: '看起来你似乎没有粘贴合适的表格内容'}));
|
||||
this.textareaInput.style.textAlign="center";
|
||||
}
|
||||
document.getElementById("loading").style.display = 'none'
|
||||
}, 50)
|
||||
}
|
||||
|
||||
handleChange = (e) => {
|
||||
this.setState({type: e.target.value})
|
||||
}
|
||||
|
||||
boxChange = (e) => {
|
||||
const state = {}
|
||||
state[e.target.value] = !this.state[e.target.value]
|
||||
this.setState(state)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="App">
|
||||
<div id="loading">
|
||||
<div id="loading-center">
|
||||
<div id="loading-center-absolute">
|
||||
<div className="object"></div>
|
||||
<div className="object"></div>
|
||||
<div className="object"></div>
|
||||
<div className="object"></div>
|
||||
<div className="object"></div>
|
||||
<div className="object"></div>
|
||||
<div className="object"></div>
|
||||
<div className="object"></div>
|
||||
<div className="object"></div>
|
||||
<div className="object"></div>
|
||||
</div>
|
||||
<div className="loading-title"><h2>正在全力计算中</h2></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="Power">
|
||||
<strong>Office Table 转 HTML Table</strong>
|
||||
<div className="PowerType">
|
||||
<label ><input type="radio" name='type' value="excel" checked={this.state.type === 'excel'}
|
||||
onChange={this.handleChange}/>Excel</label><br/>
|
||||
<label ><input type="radio" name='type' value="word" checked={this.state.type === 'word'}
|
||||
onChange={this.handleChange}/>Word/PowerPoint</label><br/>
|
||||
<label ><input type="checkbox" name='type' value="mobile" checked={this.state.mobile === true}
|
||||
onChange={this.boxChange}/>添加移动端支持</label><br/>
|
||||
<label ><input type="checkbox" name='type' value="uncss" checked={this.state.uncss === true}
|
||||
onChange={this.boxChange}/>移除未使用的CSS</label><br/>
|
||||
<label ><input type="checkbox" name='type' value="removeOfficeCss" checked={this.state.removeOfficeCss === true}
|
||||
onChange={this.boxChange}/>移除Office字体样式和专用样式(推荐)</label><br/>
|
||||
<label ><input type="checkbox" name='type' value="compress" checked={this.state.compress === true}
|
||||
onChange={this.boxChange}/>压缩代码(耗时较长)</label><br/>
|
||||
<label ><input type="checkbox" name='type' value="removeAllCSS" checked={this.state.removeAllCSS === true}
|
||||
onChange={this.boxChange}/>移除所有样式</label><br/>
|
||||
<label ><input type="checkbox" name='type' value="removeTableWidth" checked={this.state.removeTableWidth === true}
|
||||
onChange={this.boxChange}/>移除表格宽度</label><br/>
|
||||
<label ><input type="checkbox" name='type' value="percent" checked={this.state.percent === true}
|
||||
onChange={this.boxChange}/>将表格宽度转换为百分比</label>
|
||||
</div>
|
||||
</div>
|
||||
<input type="text" id="input" value={this.state.value} onFocus={this.inputFocus} onBlur={this.inputBlur} onPaste={this.inputPaste} ref={(input) => { this.textInput = input; }} readOnly/>
|
||||
<textarea id="output" onClick={this.textareaClick} ref={(input) => { this.textareaInput = input; }} value={this.state.html} readOnly></textarea>
|
||||
<iframe id="preview" title="preview"></iframe>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
9
src/App.test.js
Executable file
9
src/App.test.js
Executable file
@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
14
src/index.css
Executable file
14
src/index.css
Executable file
@ -0,0 +1,14 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
}
|
26
src/index.js
Executable file
26
src/index.js
Executable file
@ -0,0 +1,26 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import './index.css'
|
||||
import App from './App'
|
||||
import * as serviceWorker from './serviceWorker'
|
||||
|
||||
ReactDOM.render( < App / > , document.getElementById('root'))
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: http://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister()
|
||||
|
||||
// eslint-disable-next-line
|
||||
String.prototype.replaceAll = function (s1, s2) {
|
||||
// for (var j = 0; j < face.length; j++) { //考虑到含有特殊字符,不用正则
|
||||
// while (data.indexOf(face[j][1]) + 1) {
|
||||
// var index = data.indexOf(face[j][1]),
|
||||
// len = face[j][1].length,
|
||||
// str1 = data.substr(0, index),
|
||||
// str2 = data.substr(index + len);
|
||||
// data = str1 + '<img src="' + src + j + '.gif">' + str2;
|
||||
// }
|
||||
// }
|
||||
return this.replace(new RegExp(s1, "gm"), s2);
|
||||
}
|
135
src/serviceWorker.js
Executable file
135
src/serviceWorker.js
Executable file
@ -0,0 +1,135 @@
|
||||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read http://bit.ly/CRA-PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
export function register(config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit http://bit.ly/CRA-PWA'
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl, config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl, config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user