Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 2982d02
Showing
19 changed files
with
682 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"presets": [ | ||
['env',{ | ||
"targets": { | ||
"browsers": ['> 1%', 'last 2 versions'] | ||
} | ||
}] | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = { | ||
"extends": "airbnb-base" | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
"name": "x-spreadsheet", | ||
"version": "1.0.0", | ||
"description": "a javascript xpreadsheet", | ||
"private": true, | ||
"scripts": { | ||
"dev": "webpack-dev-server --open --config webpack.dev.js", | ||
"test": "./node_modules/mocha/bin/mocha --require babel-core/register test/*", | ||
"build": "webpack --config webpack.prod.js" | ||
}, | ||
"keywords": [ | ||
"javascript", | ||
"spreadsheet" | ||
], | ||
"author": "myliang", | ||
"license": "MIT", | ||
"devDependencies": { | ||
"babel-core": "^6.26.3", | ||
"babel-loader": "^8.0.2", | ||
"babel-preset-env": "^1.7.0", | ||
"clean-webpack-plugin": "^0.1.19", | ||
"css-loader": "^1.0.0", | ||
"eslint": "^5.5.0", | ||
"eslint-config-airbnb-base": "^13.1.0", | ||
"eslint-plugin-import": "^2.14.0", | ||
"file-loader": "^2.0.0", | ||
"html-webpack-plugin": "^3.2.0", | ||
"mocha": "^5.2.0", | ||
"style-loader": "^0.23.0", | ||
"webpack": "^4.17.2", | ||
"webpack-cli": "^3.1.0", | ||
"webpack-dev-server": "^3.1.7", | ||
"webpack-merge": "^4.1.4" | ||
}, | ||
"dependencies": {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
mkdir x-spreadsheet && cd x-spreadsheet | ||
npm init -y | ||
npm install webpack webpack-cli --save-dev | ||
|
||
mkdir dist src | ||
touch webpack.config.js | ||
|
||
|
||
npm install --save-dev file-loader css-loader file-loader | ||
npm install --save-dev html-webpack-plugin | ||
npm install --save-dev clean-webpack-plugin | ||
npm install --save-dev webpack-dev-server | ||
npm install --save-dev webpack-merge | ||
|
||
npm install eslint --save-dev | ||
./node_modules/.bin/eslint --init # airbnb | ||
|
||
|
||
# test mocha | ||
npm install --save-dev mocha | ||
|
||
# babel | ||
npm install --save-dev babel-loader babel-core babel-preset-env | ||
# for macha | ||
npm install --save-dev babel-register | ||
# npm install --save-dev babel-plugin-transform-runtime | ||
# npm install --save babel-runtime |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// src: include chars: [0-9], +, -, *, / | ||
// // 9+(3-1)*3+10/2 => 9 3 1-3*+ 10 2/+ | ||
const infix2suffix = (src) => { | ||
const operatorStack = []; | ||
const stack = []; | ||
for (let i = 0; i < src.length; i += 1) { | ||
const c = src.charAt(i); | ||
if (c !== ' ') { | ||
if (c >= '0' && c <= '9') { | ||
stack.push(c); | ||
} else if (c === ')') { | ||
let c1 = operatorStack.pop(); | ||
while (c1 !== '(') { | ||
stack.push(c1); | ||
c1 = operatorStack.pop(); | ||
} | ||
} else { | ||
// priority: */ > +- | ||
if (operatorStack.length > 0 && (c === '+' || c === '-')) { | ||
const last = operatorStack[operatorStack.length - 1]; | ||
if (last === '*' || last === '/') { | ||
while (operatorStack.length > 0) { | ||
stack.push(operatorStack.pop()); | ||
} | ||
} | ||
} | ||
operatorStack.push(c); | ||
} | ||
} | ||
} | ||
while (operatorStack.length > 0) { | ||
stack.push(operatorStack.pop()); | ||
} | ||
return stack; | ||
}; | ||
|
||
export default { | ||
infix2suffix, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
const alphabets = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; | ||
|
||
export default { | ||
stringAt: (index) => { | ||
let str = ''; | ||
let cindex = index; | ||
while (cindex >= alphabets.length) { | ||
cindex /= alphabets.length; | ||
cindex -= 1; | ||
str += alphabets[parseInt(cindex, 10) % alphabets.length]; | ||
} | ||
const last = index % alphabets.length; | ||
str += alphabets[last]; | ||
return str; | ||
}, | ||
indexAt: (str) => { | ||
let ret = 0; | ||
for (let i = 0; i < str.length - 1; i += 1) { | ||
const cindex = str.charCodeAt(i) - 65; | ||
const exponet = str.length - 1 - i; | ||
ret += (alphabets.length ** exponet) + alphabets.length * cindex; | ||
} | ||
ret += str.charCodeAt(str.length - 1) - 65; | ||
return ret; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import alphabet from './alphabet'; | ||
|
||
// B10 => x,y | ||
const expr2xy = (src) => { | ||
let x = ''; | ||
let y = ''; | ||
for (let i = 0; i < src.length; i += 1) { | ||
if (src.charAt(i) >= '0' && src.charAt(i) <= '9') { | ||
y += src.charAt(i); | ||
} else { | ||
x += src.charAt(i); | ||
} | ||
} | ||
return [alphabet.indexAt(x), parseInt(y, 10)]; | ||
}; | ||
|
||
// Converting infix expression to a suffix expression | ||
// src: AVERAGE(SUM(A1,A2), B1) + 50 + B20 | ||
// return: [A1, A2], SUM[, B1],AVERAGE,50,+,B20,+ | ||
const infixExprToSuffixExpr = (src) => { | ||
const operatorStack = []; | ||
const stack = []; | ||
let subStrs = []; // SUM, A1, B2, 50 ... | ||
let fnArgType = 0; // 1 => , 2 => : | ||
let fnArgsLen = 1; // A1,A2,A3... | ||
for (let i = 0; i < src.length; i += 1) { | ||
const c = src.charAt(i); | ||
if (c !== ' ') { | ||
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')) { | ||
subStrs.push(c); | ||
} else if (c === '"') { | ||
i += 1; | ||
while (src.charAt(i) !== '"') { | ||
subStrs.push(src.charAt(i)); | ||
i += 1; | ||
} | ||
stack.push(`"${subStrs.join('')}`); | ||
subStrs = []; | ||
} else { | ||
if (subStrs.length > 0) { | ||
stack.push(subStrs.join('')); | ||
} | ||
if (c === ')') { | ||
let c1 = operatorStack.pop(); | ||
if (fnArgType === 2) { | ||
// fn argument range => A1:B5 | ||
const [ex, ey] = expr2xy(stack.pop()); | ||
const [sx, sy] = expr2xy(stack.pop()); | ||
// console.log('::', sx, sy, ex, ey); | ||
let rangelen = 0; | ||
for (let x = sx; x <= ex; x += 1) { | ||
for (let y = sy; y <= ey; y += 1) { | ||
stack.push(alphabet.stringAt(x) + y); | ||
rangelen += 1; | ||
} | ||
} | ||
stack.push([c1, rangelen]); | ||
} else if (fnArgType === 1) { | ||
// fn argument => A1,A2,B5 | ||
stack.push([c1, fnArgsLen]); | ||
fnArgsLen = 1; | ||
} else { | ||
while (c1 !== '(') { | ||
stack.push(c1); | ||
c1 = operatorStack.pop(); | ||
} | ||
} | ||
fnArgType = 0; | ||
} else if (c === ':') { | ||
fnArgType = 2; | ||
} else if (c === ',') { | ||
fnArgType = 1; | ||
fnArgsLen += 1; | ||
} else if (c === '(' && subStrs.length > 0) { | ||
// function | ||
stack.pop(); | ||
operatorStack.push(subStrs.join('')); | ||
} else { | ||
// priority: */ > +- | ||
if (operatorStack.length > 0 && (c === '+' || c === '-')) { | ||
const last = operatorStack[operatorStack.length - 1]; | ||
if (last === '*' || last === '/') { | ||
while (operatorStack.length > 0) { | ||
stack.push(operatorStack.pop()); | ||
} | ||
} | ||
} | ||
operatorStack.push(c); | ||
} | ||
subStrs = []; | ||
} | ||
} | ||
} | ||
if (subStrs.length > 0) { | ||
stack.push(subStrs.join('')); | ||
} | ||
while (operatorStack.length > 0) { | ||
stack.push(operatorStack.pop()); | ||
} | ||
return stack; | ||
}; | ||
|
||
const evalSubExpr = (subExpr, cellRender) => { | ||
if (subExpr[0] >= '0' && subExpr[0] <= '9') { | ||
return subExpr; | ||
} | ||
if (subExpr[0] === '"') { | ||
return subExpr.substring(1); | ||
} | ||
const [x, y] = expr2xy(subExpr); | ||
return cellRender(x, y); | ||
}; | ||
|
||
// evaluate the suffix expression | ||
// srcStack: <= infixExprToSufixExpr | ||
// formulaMap: {'SUM': {}, ...} | ||
// cellRender: (x, y) => {} | ||
const evalSuffixExpr = (srcStack, formulaMap, cellRender) => { | ||
const stack = []; | ||
for (let i = 0; i < srcStack.length; i += 1) { | ||
if (srcStack[i] === '+') { | ||
stack.push(stack.pop() + stack.pop()); | ||
} else if (srcStack[i] === '-') { | ||
stack.push(stack.pop() - stack.pop()); | ||
} else if (srcStack[i] === '*') { | ||
stack.push(stack.pop() * stack.pop()); | ||
} else if (srcStack[i] === '/') { | ||
stack.push(stack.pop() / stack.pop()); | ||
} else if (Array.isArray(srcStack[i])) { | ||
const [formula, len] = srcStack[i]; | ||
stack.push(formulaMap[formula].render(srcStack.slice(i - len, i))); | ||
} else { | ||
stack.push(evalSubExpr(srcStack[i], cellRender)); | ||
} | ||
} | ||
return stack[0]; | ||
}; | ||
|
||
const cellRender = (src, formulaMap, getCellText) => { | ||
if (src[0] === '=') { | ||
const stack = infixExprToSuffixExpr(src); | ||
console.log('suffixExpr:', stack); | ||
const cb = (x, y) => cellRender(getCellText(x, y), formulaMap, getCellText); | ||
return evalSuffixExpr(stack, formulaMap, cb); | ||
} | ||
return src; | ||
}; | ||
|
||
export default { | ||
render: cellRender, | ||
}; | ||
export { | ||
infixExprToSuffixExpr, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
const baseFonts = [ | ||
{ key: 'Comic Sans MS', title: 'Comic Sans MS' }, | ||
{ key: 'Arial', title: 'Arial' }, | ||
{ key: 'Courier New', title: 'Courier New' }, | ||
{ key: 'Verdana', title: 'Verdana' }, | ||
]; | ||
|
||
const fonts = (ary = []) => { | ||
const map = {}; | ||
baseFonts.concat(ary).forEach((f) => { | ||
map[f.key] = f; | ||
}); | ||
return map; | ||
}; | ||
|
||
export default {}; | ||
export { | ||
fonts, | ||
baseFonts, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
const formatStringRender = v => v; | ||
|
||
const formatNumberRender = (v) => { | ||
if (/^(-?\d*.?\d*)$/.test(v)) { | ||
const v1 = Number(v).toFixed(2).toString(); | ||
const [first, ...parts] = v1.split('\\.'); | ||
return [first.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'), ...parts]; | ||
} | ||
return v; | ||
}; | ||
|
||
const baseFormats = [ | ||
{ | ||
key: 'normal', | ||
title: 'Normal', | ||
render: formatStringRender, | ||
}, | ||
{ | ||
key: 'text', | ||
title: 'Text', | ||
render: formatStringRender, | ||
}, | ||
{ | ||
key: 'number', | ||
title: 'Number', | ||
label: '1,000.12', | ||
render: formatNumberRender, | ||
}, | ||
{ | ||
key: 'percent', | ||
title: 'Percent', | ||
label: '10.12%', | ||
render: v => `${v}%`, | ||
}, | ||
{ | ||
key: 'RMB', | ||
title: 'RMB', | ||
label: '¥10.00', | ||
render: v => `¥${formatNumberRender(v)}`, | ||
}, | ||
{ | ||
key: 'USD', | ||
title: 'USD', | ||
label: '$10.00', | ||
render: v => `$${formatNumberRender(v)}`, | ||
}, | ||
]; | ||
|
||
const formats = (ary = []) => { | ||
const map = {}; | ||
baseFormats.concat(ary).forEach((f) => { | ||
map[f.key] = f; | ||
}); | ||
return map; | ||
}; | ||
|
||
export default { | ||
}; | ||
export { | ||
formats, | ||
baseFormats, | ||
}; |
Oops, something went wrong.