mirror of https://gitee.com/godoos/godoos.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
990 lines
38 KiB
990 lines
38 KiB
window.onload = function () {
|
|
Calculator.initCache();
|
|
Calculator.initListeners();
|
|
};
|
|
|
|
//全局计算器对象
|
|
var Calculator = (function () {
|
|
var cal = {
|
|
//计算器按键编码
|
|
keyCodes: {
|
|
0: '0',
|
|
1: '1',
|
|
2: '2',
|
|
3: '3',
|
|
4: '4',
|
|
5: '5',
|
|
6: '6',
|
|
7: '7',
|
|
8: '8',
|
|
9: '9',
|
|
10: '.',
|
|
11: '±',
|
|
12: '=',
|
|
13: '+',
|
|
14: '-',
|
|
15: '*',
|
|
16: '/',
|
|
17: '%',
|
|
18: '√',
|
|
19: 'x2',
|
|
20: '1/x',
|
|
21: '(',
|
|
22: ')',
|
|
23: 'yroot',
|
|
24: 'n!',
|
|
25: 'Exp',
|
|
26: '^',
|
|
27: 'sin',
|
|
28: 'cos',
|
|
29: 'tan',
|
|
30: 'powten',
|
|
31: 'log',
|
|
32: 'sinh',
|
|
33: 'cosh',
|
|
34: 'tanh',
|
|
35: 'π',
|
|
36: '↑',
|
|
37: 'CE',
|
|
38: 'C',
|
|
39: 'Back',
|
|
//以下是程序员型特有的按键
|
|
40: 'A',
|
|
41: 'B',
|
|
42: 'C',
|
|
43: 'D',
|
|
44: 'E',
|
|
45: 'F',
|
|
46: '&',
|
|
47: '|',
|
|
48: '~'
|
|
},
|
|
//映射用于显示的操作符,比如计算时用*,而显示时x更好
|
|
operatorFacade: {
|
|
13: '+',
|
|
14: '-',
|
|
15: '×',
|
|
16: '÷',
|
|
17: '%',
|
|
23: 'yroot',
|
|
26: '^',
|
|
46: '&',
|
|
47: '|'
|
|
},
|
|
//当前计算器的类型1 --> 标准型, 2-->科学型, 3-->程序员型,默认标准型
|
|
type: 1,
|
|
//计算器类型前缀,用于从页面获取元素
|
|
typePrefix: {
|
|
1: "std-",
|
|
2: "sci-",
|
|
3: "pro-"
|
|
},
|
|
//记录每个类型的计算器的事件监听是否已经绑定,key:typpe数值,value:默认标准型是true(已加载)
|
|
hasInited: {
|
|
1: true,
|
|
2: false,
|
|
3: false
|
|
},
|
|
//常量
|
|
constants: {
|
|
//鼠标悬停时的颜色
|
|
mouseHoverColor: "#CFCFCF",
|
|
//计算器第一行和下面其它行的颜色是不同的,这个是第一行的背景颜色
|
|
firstMouseOutColor: "#F2F2F2",
|
|
//剩余各行的背景颜色
|
|
mouseOutColor: "#E6E6E6"
|
|
},
|
|
cache: {
|
|
//输入内容显示元素
|
|
showInput: null,
|
|
//上一步计算结果显示区域
|
|
preStep: null,
|
|
//显示四种进制数值的span,只在程序员型有效
|
|
scaleSpans: null
|
|
},
|
|
/**
|
|
* 获取cache.showInput的内容
|
|
* @return String
|
|
*/
|
|
getShowInput: function () {
|
|
return cal.cache.showInput.innerHTML;
|
|
},
|
|
/**
|
|
* 设置showInput的值
|
|
* @param value
|
|
*/
|
|
setShowInput: function (value) {
|
|
cal.cache.showInput.innerHTML = value;
|
|
},
|
|
/**
|
|
* 获取cache.preStep的内容
|
|
* @return String
|
|
*/
|
|
getPreStep: function () {
|
|
return cal.cache.preStep.innerHTML;
|
|
},
|
|
setPreStep: function (value) {
|
|
cal.cache.preStep.innerHTML = value;
|
|
},
|
|
//操作数栈
|
|
operandStack: [],
|
|
//运算符栈
|
|
operatorStack: [],
|
|
//上一次输入是否是二元运算符,如果是并且再次输入二元运算符,那么忽略此次输入
|
|
isPreInputBinaryOperator: false,
|
|
//上次按键是否是一元操作
|
|
isPreInputUnaryOperator: false,
|
|
//等号不可以连按
|
|
isPreInputEquals: false,
|
|
//如果为true,那么接下来输入的数字需要覆盖在showInput上,而不是追加
|
|
//上一次计算的结果(=)
|
|
preResult: 0,
|
|
//当前使用的进制(只在程序员中有效),默认10进制(DEC)
|
|
currentScale: 10,
|
|
isOverride: false,
|
|
//int校验
|
|
intPattern: /^-?\d+$/,
|
|
//小数校验
|
|
floatPattern: /^-?\d+\.\d+$/,
|
|
//科学计数法校验
|
|
scientificPattern: /^\d+\.\d+e(\+|-)\d+$/,
|
|
//校验16进制数字
|
|
hexPattern: /^[0-9A-F]+$/,
|
|
//辅助判断运算符的优先级
|
|
operatorPriority: {
|
|
")": 0,
|
|
"|": 1,
|
|
"&": 2,
|
|
"+": 3,
|
|
"-": 3,
|
|
"*": 4,
|
|
"%": 4,
|
|
"/": 4,
|
|
"^": 5,
|
|
"yroot": 5,
|
|
"(": 6
|
|
},
|
|
/**
|
|
* 初始化缓存对象(cal.cache)
|
|
*/
|
|
initCache: function () {
|
|
var prefix = cal.typePrefix[cal.type];
|
|
cal.cache.showInput = document.getElementById(prefix + "show-input");
|
|
cal.cache.preStep = document.getElementById(prefix + "pre-step");
|
|
if (cal.type == 3) {
|
|
cal.cache.scaleSpans = document.getElementById("pro-scales").getElementsByTagName("span");
|
|
}
|
|
},
|
|
//各种事件监听函数
|
|
listeners: {
|
|
/**
|
|
* 鼠标悬停在按键上的变色效果
|
|
*/
|
|
mouseHoverListener: function (e) {
|
|
var event = e || window.event;
|
|
event.currentTarget.style.backgroundColor = cal.constants.mouseHoverColor;
|
|
},
|
|
/**
|
|
* 鼠标从上排符号中移出的变色效果
|
|
*/
|
|
firstMouseOutListener: function (e) {
|
|
var event = e || window.event;
|
|
event.currentTarget.style.backgroundColor = cal.constants.firstMouseOutColor;
|
|
},
|
|
/**
|
|
* 鼠标从下排数字、符号中移出的变色效果
|
|
*/
|
|
mouseOutListener: function (e) {
|
|
var event = e || window.event;
|
|
event.currentTarget.style.backgroundColor = cal.constants.mouseOutColor;
|
|
},
|
|
/**
|
|
* 按键按下事件监听
|
|
*/
|
|
keyPressListener: function (e) {
|
|
var event = e || window.event;
|
|
cal.handleKey(event.currentTarget.value);
|
|
},
|
|
/**
|
|
* 显示/隐藏计算器类型选择栏
|
|
*/
|
|
toggleTypeBarListener: function () {
|
|
var bar = document.getElementById(cal.typePrefix[cal.type] + "type-bar");
|
|
if (bar.style.display === "block") {
|
|
bar.style.display = "none";
|
|
} else {
|
|
bar.style.display = "block";
|
|
}
|
|
},
|
|
/**
|
|
* 切换计算器类型监听器
|
|
*/
|
|
switchTypeListener: function (e) {
|
|
var event = e || window.event;
|
|
cal.switchType(parseInt(event.currentTarget.value));
|
|
},
|
|
/**
|
|
* 切换进制(程序员专用)
|
|
*/
|
|
switchScaleListener: function (e) {
|
|
var event = e || window.event;
|
|
var scales = document.getElementById("pro-scales").getElementsByTagName("div"),
|
|
//此处应该使用currentTarget属性,因为target属性在绑定事件的元素有子元素的情况下会返回子元素
|
|
scale = parseInt(event.currentTarget.getAttribute("scale")),
|
|
oldScale = cal.currentScale;
|
|
//切换选中样式
|
|
for (var i = 0, l = scales.length; i < l; ++i) {
|
|
scales[i].removeAttribute("class");
|
|
}
|
|
event.currentTarget.setAttribute("class", "scale-active");
|
|
var lis, btns;
|
|
if (scale === 16) {
|
|
//处理上排6个16进制数字
|
|
cal.listeners._initFirstRowListeners();
|
|
if (oldScale < 10) {
|
|
cal.listeners._initSecondRowListeners();
|
|
}
|
|
} else if (scale === 10) {
|
|
if (oldScale === 16) {
|
|
lis = document.getElementById("pro-top-symbol").getElementsByTagName("li");
|
|
cal.disableButtons(lis, cal.listeners.firstMouseOutListener);
|
|
} else {
|
|
cal.listeners._initSecondRowListeners();
|
|
}
|
|
} else if (scale === 8) {
|
|
if (oldScale > 8) {
|
|
lis = document.getElementById("pro-top-symbol").getElementsByTagName("li");
|
|
cal.disableButtons(lis, cal.listeners.firstMouseOutListener);
|
|
//禁用8和9
|
|
btns = cal.getElementsByAttribute("li", "oct-disable", document.getElementById("pro-num-symbol"));
|
|
cal.disableButtons(btns, cal.listeners.mouseOutListener);
|
|
} else {
|
|
cal.listeners._initSecondRowListeners();
|
|
}
|
|
} else if (scale === 2) {
|
|
if (oldScale === 16) {
|
|
lis = document.getElementById("pro-top-symbol").getElementsByTagName("li");
|
|
cal.disableButtons(lis, cal.listeners.firstMouseOutListener);
|
|
}
|
|
//禁用2-9
|
|
btns = cal.getElementsByAttribute("li", "bin-disable", document.getElementById("pro-num-symbol"));
|
|
cal.disableButtons(btns, cal.listeners.mouseOutListener);
|
|
}
|
|
cal.currentScale = scale;
|
|
},
|
|
/**
|
|
* 初始化第一排操运算符事件监听
|
|
* @private
|
|
*/
|
|
_initFirstRowListeners: function () {
|
|
var lis = document.getElementById(cal.typePrefix[cal.type] + "top-symbol").getElementsByTagName("li");
|
|
cal.rebuildButtons(lis, cal.listeners.firstMouseOutListener);
|
|
},
|
|
/**
|
|
* 初始化第二排运算符事件监听
|
|
* @private
|
|
*/
|
|
_initSecondRowListeners: function () {
|
|
var lis = document.getElementById(cal.typePrefix[cal.type] + "num-symbol").getElementsByTagName("li");
|
|
cal.rebuildButtons(lis, cal.listeners.mouseOutListener);
|
|
if (cal.type === 3) {
|
|
//程序员型的小数点是禁用的
|
|
cal.disableButtons([document.getElementById("pro-point")], cal.listeners.mouseOutListener);
|
|
}
|
|
}
|
|
},
|
|
//初始化事件监听器
|
|
initListeners: function () {
|
|
var prefix = cal.typePrefix[cal.type];
|
|
//设置上排运算符事件监听,如果是程序员型,因为默认是10进制,而上排是16进制数字,所以不需要设置事件监听
|
|
if (cal.type < 3) {
|
|
cal.listeners._initFirstRowListeners();
|
|
}
|
|
//设置下面一栏数字、四则运算事件监听
|
|
cal.listeners._initSecondRowListeners();
|
|
//显示/隐藏计算器类型选择侧边栏
|
|
cal.addEvent(document.getElementById(prefix + "show-bar"), "click", cal.listeners.toggleTypeBarListener);
|
|
//为侧边栏下的li绑定切换类型事件
|
|
var bar = document.getElementById(prefix + "type-bar");
|
|
lis = bar.getElementsByTagName("li");
|
|
var li;
|
|
for (var i = 0, l = lis.length; i < l; ++i) {
|
|
li = lis[i];
|
|
//非当前类型才有必要绑定事件
|
|
if (li.className !== "active") {
|
|
cal.addEvent(li, "click", cal.listeners.switchTypeListener);
|
|
}
|
|
}
|
|
//加载程序员型特有的
|
|
if (cal.type === 3) {
|
|
var scales = document.getElementById("pro-scales").getElementsByTagName("div"),
|
|
scale;
|
|
for (i = 0, l = scales.length; i < l; ++i) {
|
|
scale = scales[i];
|
|
cal.addEvent(scale, "click", cal.listeners.switchScaleListener);
|
|
}
|
|
}
|
|
},
|
|
/**
|
|
* 相应按键按下事件
|
|
* @param value 按键的value值(即其keyCode)
|
|
*/
|
|
handleKey: function (value) {
|
|
var keyCode = parseInt(value);
|
|
//如果是一个数字或者小数点,直接显示出来
|
|
if (keyCode < 11 || (keyCode > 39 && keyCode < 46)) {
|
|
cal.showInput(cal.keyCodes[keyCode]);
|
|
if (cal.type === 3) {
|
|
//如果是程序员型,那么需要同步显示4中进制的值
|
|
cal.showScales(cal.getShowInput());
|
|
}
|
|
} else {
|
|
switch (keyCode) {
|
|
//正负号
|
|
case 11:
|
|
cal.unaryOperate(function (oldValue) {
|
|
oldValue += "";
|
|
if (oldValue === "0") {
|
|
return [oldValue];
|
|
}
|
|
if (oldValue.charAt(0) === '-') {
|
|
return [oldValue.substring(1)];
|
|
} else {
|
|
return ["-" + oldValue];
|
|
}
|
|
});
|
|
break;
|
|
//开根下
|
|
case 18:
|
|
cal.unaryOperate(function (si) {
|
|
return [Math.sqrt(si), "sqrt"];
|
|
});
|
|
break;
|
|
//平方
|
|
case 19:
|
|
cal.unaryOperate(function (si) {
|
|
return [Math.pow(si, 2), "sqr"];
|
|
});
|
|
break;
|
|
//取倒数
|
|
case 20:
|
|
cal.unaryOperate(function (si) {
|
|
return [si === 0 ? "0不能作为被除数" : 1 / si, "1/"];
|
|
});
|
|
break;
|
|
//阶乘
|
|
case 24:
|
|
cal.unaryOperate(function (si) {
|
|
if (si < 0) {
|
|
si = (0 - si);
|
|
}
|
|
if (cal.isFloat(si + "")) {
|
|
si = Math.floor(si);
|
|
}
|
|
return [cal.fact(si), "fact"];
|
|
});
|
|
break;
|
|
//Exp 转为科学计数法表示
|
|
case 25:
|
|
cal.unaryOperate(function (si) {
|
|
return [si.toExponential(7)];
|
|
});
|
|
break;
|
|
//sin
|
|
case 27:
|
|
cal.unaryOperate(function (si) {
|
|
return [Math.sin(si), "sin"];
|
|
});
|
|
break;
|
|
//cos
|
|
case 28:
|
|
cal.unaryOperate(function (si) {
|
|
return [Math.cos(si), "cos"];
|
|
});
|
|
break;
|
|
//tan
|
|
case 29:
|
|
cal.unaryOperate(function (si) {
|
|
return [Math.tan(si), "tan"];
|
|
});
|
|
break;
|
|
//10的x次方
|
|
case 30:
|
|
cal.unaryOperate(function (si) {
|
|
return [Math.pow(10, si), "powten"];
|
|
});
|
|
break;
|
|
//log
|
|
case 31:
|
|
cal.unaryOperate(function (si) {
|
|
//js的Math.log是e的对数,Windows计算器是10的对数,此处参考Windows
|
|
return [Math.log10(si), "log"];
|
|
});
|
|
break;
|
|
//sinh(双曲正弦函数)
|
|
case 32:
|
|
cal.unaryOperate(function (si) {
|
|
return [Math.sinh(si), "sinh"];
|
|
});
|
|
break;
|
|
//cosh(双曲余弦函数)
|
|
case 33:
|
|
cal.unaryOperate(function (si) {
|
|
return [Math.cosh(si), "cosh"];
|
|
});
|
|
break;
|
|
//tanh(双曲余切函数)
|
|
case 34:
|
|
cal.unaryOperate(function (si) {
|
|
return [Math.tanh(si), "tanh"];
|
|
});
|
|
break;
|
|
//π
|
|
case 35:
|
|
cal.unaryOperate(function (si) {
|
|
return [Math.PI];
|
|
});
|
|
break;
|
|
//按位取反(~)
|
|
case 48:
|
|
cal.unaryOperate(function (si) {
|
|
var result = eval("~" + si);
|
|
//显示四种进制的数值
|
|
cal.showScales(result);
|
|
return [result];
|
|
});
|
|
break;
|
|
//二元运算符开始
|
|
//加、减、乘、除、取余,运算比较简单,直接利用eval即可求值
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
case 16:
|
|
case 17:
|
|
//x的y次方
|
|
case 26:
|
|
//开任意次方根
|
|
case 23:
|
|
//And Or
|
|
case 46:
|
|
case 47:
|
|
if (cal.isPreInputBinaryOperator) {
|
|
break;
|
|
}
|
|
cal.isPreInputBinaryOperator = true;
|
|
cal.isOverride = true;
|
|
cal.binaryOperate(cal.keyCodes[keyCode], cal.operatorFacade[keyCode]);
|
|
break;
|
|
case 12:
|
|
cal.calculate();
|
|
break;
|
|
//ce
|
|
case 37:
|
|
cal.ce();
|
|
break;
|
|
//c
|
|
case 38:
|
|
cal.clear();
|
|
break;
|
|
//back
|
|
case 39:
|
|
cal.back();
|
|
break;
|
|
// (
|
|
case 21:
|
|
cal.setPreStep(cal.getPreStep() + " (");
|
|
cal.operatorStack.push("(");
|
|
break;
|
|
// )
|
|
case 22:
|
|
cal.rightTag();
|
|
break;
|
|
//向上箭头,把上次计算结果显示出来
|
|
case 36:
|
|
cal.setShowInput(cal.preResult);
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
/**
|
|
* 执行一元运算 比如取倒数、平方
|
|
* @param operation 具体运算回调函数
|
|
* 会向operation传递一个参数si,为用户当前的输入,同时operation函数应该返回一个数组,数组的第一个
|
|
* 元素是计算的结果,第二个元素示例sqrt,第二个参数可选
|
|
*/
|
|
unaryOperate: function (operation) {
|
|
var si = cal.getShowInput(),
|
|
result;
|
|
if (cal.isInteger(si)) {
|
|
result = operation(parseInt(si));
|
|
} else if (cal.isFloat(si) || cal.isScientific(si)) {
|
|
result = operation(parseFloat(si));
|
|
}
|
|
if (result != null) {
|
|
cal.setShowInput(cal.checkLength(result[0]));
|
|
if (result.length > 1) {
|
|
//显示prestep有两种情况:
|
|
//第一种就是这是第一次(指连续调用的第一次)调用一元函数,此时直接接在末尾即可
|
|
if (!cal.isPreInputUnaryOperator) {
|
|
cal.setPreStep(cal.getPreStep() + " " + result[1] + "(" + si + ")");
|
|
cal.isPreInputUnaryOperator = true;
|
|
} else {
|
|
//第二种就是这不是第一次,那么应该截取最后一个空格之后的内容进行替换
|
|
//比如1 + 3 + sqrt(100),那么应该从最后一个空格后替换为此次操作的内容
|
|
var pi = cal.getPreStep();
|
|
pi = pi.substring(0, pi.lastIndexOf(" "));
|
|
pi += (" " + result[1] + "(" + si + ")");
|
|
cal.setPreStep(pi);
|
|
}
|
|
}
|
|
//一元运算结束后应该覆盖
|
|
cal.isOverride = true;
|
|
}
|
|
cal.isPreInputBinaryOperator = false;
|
|
},
|
|
/**
|
|
* 二元操作(+ - * / %)
|
|
* @param operator 操作符
|
|
* @param facade 运算符门面,用于显示在preStep中
|
|
*/
|
|
binaryOperate: function (operator, facade) {
|
|
//如果是程序员型,那么需要重置scalesSpan
|
|
if (cal.type === 3) {
|
|
cal.resetScales();
|
|
}
|
|
var si = cal.getShowInput(),
|
|
pi = cal.getPreStep();
|
|
if (cal.isNumber(si)) {
|
|
//压操作数栈
|
|
cal.operandStack.push(si);
|
|
//设置preStep有三种情况:第一种上一步不是一元操作,那么需要设置si,第二种是一元操作,那么由于一元操作会把
|
|
//函数表达式(比如sqrt(100))设置到preStep,所以不需要再次设置si
|
|
//第三种就是如果最后一位是右括号,那么也不需要设置si
|
|
cal.setPreStep(cal.getPreStep() + ((cal.isPreInputUnaryOperator || pi.charAt(pi.length - 1) === ")") ?
|
|
(" " + facade) : (" " + si + " " + facade)));
|
|
var preOp = cal.operatorStack.pop();
|
|
if (preOp != null) {
|
|
var op = cal.operatorPriority[operator],
|
|
pp = cal.operatorPriority[preOp];
|
|
//如果当前运算符优先级更高,那么只需压栈不需要计算
|
|
if (op > pp) {
|
|
cal.operatorStack.push(preOp);
|
|
}
|
|
//两者的优先级相等并且高于3(加减),那么只需要计算一步
|
|
else if (op > 3 && op === pp) {
|
|
cal.operatorStack.push(preOp);
|
|
cal.travelStack(1);
|
|
} else {
|
|
cal.operatorStack.push(preOp);
|
|
cal.setShowInput(cal.checkLength(cal.travelStack(null, op)));
|
|
}
|
|
}
|
|
cal.operatorStack.push(operator);
|
|
}
|
|
cal.isPreInputUnaryOperator = false;
|
|
cal.isPreInputEquals = false;
|
|
},
|
|
/**
|
|
* 按下=时计算最终结果
|
|
*/
|
|
calculate: function () {
|
|
if (!cal.isPreInputEquals) {
|
|
var si = cal.getShowInput(),
|
|
result;
|
|
if (cal.isNumber(si)) {
|
|
cal.operandStack.push(si);
|
|
result = cal.checkLength(cal.travelStack());
|
|
cal.setShowInput(result);
|
|
cal.preResult = result;
|
|
cal.setPreStep(" ");
|
|
//程序员型需要把计算结果的四种进制值显示出来
|
|
if (cal.type === 3) {
|
|
cal.showScales(result);
|
|
}
|
|
cal.isOverride = true;
|
|
}
|
|
cal._reset();
|
|
cal.isPreInputEquals = true;
|
|
}
|
|
},
|
|
/**
|
|
* 访问运算栈,返回计算结果
|
|
* @param level 计算的层数,如果不指定,那么遍历整个栈
|
|
* @param minPri(最小/截止优先级) 此参数针对下面的情况:
|
|
* 2 + 2 X 3 X 2 ^ 2 X 2,由于最后一个运算符是X,优先级比^低,所以触发了对操作栈的遍历,但是不能全部遍历,应该遍历到第一个X停止
|
|
* 如果不停止得到的将是错误的26 X 2 = 52,正确结果是2 + 24 X 2 = 50
|
|
* @return Number
|
|
* @private
|
|
*/
|
|
travelStack: function (level, minPri) {
|
|
var op, f, s,
|
|
//result取操作数栈栈顶,因为防止在下列情况9 X (6 + 时出现undefined
|
|
result = cal.operandStack[cal.operandStack.length - 1],
|
|
l = level || cal.operatorStack.length,
|
|
p = minPri || 0;
|
|
for (var i = 0; i < l; ++i) {
|
|
op = cal.operatorStack.pop();
|
|
//遇到minPri或左括号立即停止,左括号也需要再次压入,因为只有一个右括号才能抵消一个左括号
|
|
if (cal.operatorPriority[op] < p || op === "(") {
|
|
cal.operatorStack.push(op);
|
|
break;
|
|
}
|
|
s = cal.operandStack.pop();
|
|
f = cal.operandStack.pop();
|
|
result = cal._stackHelper(f, s, op);
|
|
cal.operandStack.push(result);
|
|
}
|
|
return result;
|
|
},
|
|
/**
|
|
* 输入了一个右括号
|
|
*/
|
|
rightTag: function () {
|
|
var si = cal.getShowInput();
|
|
if (cal.isNumber(si)) {
|
|
cal.setPreStep(cal.getPreStep() + (" " + si + " )"));
|
|
cal.operandStack.push(si);
|
|
//遍历计算操作栈,直至遇到左括号
|
|
var op = cal.operatorStack.pop(),
|
|
f, s, result;
|
|
while (op !== "(" && op != null) {
|
|
s = cal.operandStack.pop();
|
|
f = cal.operandStack.pop();
|
|
result = cal._stackHelper(f, s, op);
|
|
cal.operandStack.push(result);
|
|
op = cal.operatorStack.pop();
|
|
}
|
|
//此处应该直接把小括号的计算内容弹出,因为此结果显示在了showInput中,而再次执行二元操作时会先有一个压栈的操作,
|
|
// 并且执行=时也是根据showInput内容计算的
|
|
cal.setShowInput(cal.checkLength(cal.operandStack.pop()));
|
|
}
|
|
},
|
|
/**
|
|
* 辅助进行一次栈运算
|
|
* @param f 第一个操作数
|
|
* @param s 第二个操作数
|
|
* @param op 运算符
|
|
* @return 返回运算结果
|
|
* @private
|
|
*/
|
|
_stackHelper: function (f, s, op) {
|
|
var result;
|
|
if (op === "^") {
|
|
result = Math.pow(f, s);
|
|
} else if (op === "yroot") {
|
|
result = Math.pow(f, 1 / s);
|
|
}
|
|
//+ - X / %5中操作
|
|
else {
|
|
//如果是程序员型,那么需要考虑进制的问题
|
|
if (cal.type === 3) {
|
|
var scale = cal.currentScale,
|
|
fi, si;
|
|
if (scale === 10) {
|
|
result = eval(f + op + s);
|
|
} else if (scale === 16) {
|
|
fi = parseInt(f, 16);
|
|
si = parseInt(s, 16);
|
|
result = eval(fi + op + si).toString(16);
|
|
} else if (scale === 8) {
|
|
fi = parseInt(f, 8);
|
|
si = parseInt(s, 8);
|
|
result = eval(fi + op + si).toString(8);
|
|
} else {
|
|
fi = parseInt(f, 2);
|
|
si = parseInt(s, 2);
|
|
result = eval(fi + op + si).toString(2);
|
|
}
|
|
} else {
|
|
result = eval(f + op + s);
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
/**
|
|
* 确保结果长度不大于13,如果超出,以科学计数法形式显示(小数点后7位)
|
|
* @param value 需要检查的结果
|
|
*/
|
|
checkLength: function (value) {
|
|
var valueStr = value + "";
|
|
if (cal.isFloat(valueStr)) {
|
|
valueStr = valueStr.replace(/0+$/, "");
|
|
}
|
|
return valueStr.length > 12 ? value.toExponential(7) : valueStr;
|
|
},
|
|
//CE
|
|
ce: function () {
|
|
cal.setShowInput("0");
|
|
if (cal.type === 3) {
|
|
cal.resetScales();
|
|
}
|
|
},
|
|
//C
|
|
clear: function () {
|
|
cal.setShowInput("0");
|
|
cal.setPreStep(" ");
|
|
cal._reset();
|
|
if (cal.type === 3) {
|
|
cal.resetScales();
|
|
}
|
|
},
|
|
/**
|
|
* 清空四个进制的值
|
|
* @private
|
|
*/
|
|
resetScales: function () {
|
|
for (var i = 0; i < 4; i++) {
|
|
cal.cache.scaleSpans[i].innerHTML = "0";
|
|
}
|
|
},
|
|
back: function () {
|
|
var oldValue = cal.cache.showInput.innerText;
|
|
cal.setShowInput(oldValue.length < 2 ? "0" : oldValue.substring(0, oldValue.length - 1));
|
|
},
|
|
/**
|
|
* 当计算器类型是程序员时,需要同步显示四种进制的值
|
|
* @param num 需要显示的数字
|
|
*/
|
|
showScales: function (num) {
|
|
var result = cal.calculateScales(num),
|
|
spans = cal.cache.scaleSpans;
|
|
for (var i = 0; i < 4; ++i) {
|
|
spans[i].innerHTML = result[i];
|
|
}
|
|
},
|
|
/**
|
|
* 根据当前进制分别计算出四种进制的值
|
|
* @param num 需要计算的值
|
|
* @return Array 共4个元素,依次为16、10、8、2进制的值
|
|
*/
|
|
calculateScales: function (num) {
|
|
var scale = cal.currentScale,
|
|
result = [],
|
|
i;
|
|
if (scale === 10) {
|
|
i = parseInt(num);
|
|
result[0] = i.toString(16);
|
|
result[1] = i;
|
|
result[2] = i.toString(8);
|
|
result[3] = i.toString(2);
|
|
} else if (scale === 16) {
|
|
//先转成10进制,然后再转成其它进制
|
|
i = parseInt(num, 16);
|
|
result[0] = num;
|
|
result[1] = i;
|
|
result[2] = i.toString(8);
|
|
result[3] = i.toString(2);
|
|
} else if (scale === 8) {
|
|
i = parseInt(num, 8);
|
|
result[0] = i.toString(16);
|
|
result[1] = i;
|
|
result[2] = num;
|
|
result[3] = i.toString(2);
|
|
} else {
|
|
i = parseInt(num, 2);
|
|
result[0] = i.toString(16);
|
|
result[1] = i;
|
|
result[2] = i.toString(8);
|
|
result[3] = num;
|
|
}
|
|
return result;
|
|
},
|
|
/**
|
|
* 校验字符串是否是数字
|
|
* @param str
|
|
* @return 是返回true
|
|
*/
|
|
isNumber: function (str) {
|
|
return cal.isInteger(str) || cal.isFloat(str) || cal.isScientific(str) || cal.isHex(str);
|
|
},
|
|
/**
|
|
* 校验是否是整数
|
|
* @param str
|
|
*/
|
|
isInteger: function (str) {
|
|
return str.match(cal.intPattern);
|
|
},
|
|
/**
|
|
* 校验是否是小数
|
|
* @param str
|
|
*/
|
|
isFloat: function (str) {
|
|
return str.match(cal.floatPattern);
|
|
},
|
|
/**
|
|
* 是否是科学计数法
|
|
* @param str
|
|
*/
|
|
isScientific: function (str) {
|
|
return str.match(cal.scientificPattern);
|
|
},
|
|
/**
|
|
* 是否是16进制数字
|
|
* @param str
|
|
*/
|
|
isHex: function (str) {
|
|
return str.match(cal.hexPattern);
|
|
},
|
|
/**
|
|
* 显示输入的内容
|
|
* 用于相应数字/小数点按键
|
|
* @param value 按键的内容,不是keyCode
|
|
*/
|
|
showInput: function (value) {
|
|
var oldValue = cal.getShowInput();
|
|
var newValue = oldValue;
|
|
if (cal.isOverride) {
|
|
//既然是覆盖,那么如果直接输入.那么肯定是0.x
|
|
if (value === ".") {
|
|
newValue = "0.";
|
|
} else {
|
|
newValue = value;
|
|
}
|
|
} else if (oldValue.length < 13) {
|
|
if (oldValue === "0") {
|
|
if (value === ".") {
|
|
newValue = "0.";
|
|
} else {
|
|
newValue = value;
|
|
}
|
|
} else {
|
|
newValue += value;
|
|
}
|
|
}
|
|
cal.setShowInput(newValue);
|
|
cal.isOverride = false;
|
|
cal.isPreInputBinaryOperator = false;
|
|
cal.isPreInputUnaryOperator = false;
|
|
cal.isPreInputEquals = false;
|
|
},
|
|
/**
|
|
* 切换计算器类型
|
|
* @param type int 要切换到的类型
|
|
*/
|
|
switchType: function (type) {
|
|
//关闭选择栏
|
|
var oldPrefix = cal.typePrefix[cal.type];
|
|
document.getElementById(oldPrefix + "type-bar").style.display = "none";
|
|
//切换面板
|
|
document.getElementById(oldPrefix + "main").style.display = "none";
|
|
document.getElementById(cal.typePrefix[type] + "main").style.display = "block";
|
|
cal.type = type;
|
|
if (!cal.hasInited[type]) {
|
|
cal.initListeners();
|
|
cal.hasInited[type] = true;
|
|
}
|
|
cal.initCache();
|
|
cal._reset();
|
|
},
|
|
/**
|
|
* 重置各个标志变量以及操作栈
|
|
* @private
|
|
*/
|
|
_reset: function () {
|
|
cal.operandStack = [];
|
|
cal.operatorStack = [];
|
|
cal.isPreInputBinaryOperator = false;
|
|
cal.isPreInputUnaryOperator = false;
|
|
cal.isPreInputEquals = false;
|
|
},
|
|
/**
|
|
* 工具方法,为element添加事件处理函数
|
|
* @param element 需要添加事件的dom元素
|
|
* @param name name事件名称(不含on)
|
|
* @param handler 事件处理函数
|
|
*/
|
|
addEvent: function (element, name, handler) {
|
|
if (window.addEventListener) {
|
|
element.addEventListener(name, handler);
|
|
} else if (window.attachEvent) {
|
|
element.attachEvent("on" + name, handler);
|
|
}
|
|
},
|
|
/**
|
|
* 工具方法,为element移除特定的事件监听
|
|
* @param element 需要移除事件监听的dom元素
|
|
* @param name 事件名,没有"on"
|
|
* @param handler 需要移除的处理函数
|
|
*/
|
|
removeEvent: function (element, name, handler) {
|
|
if (window.removeEventListener) {
|
|
element.removeEventListener(name, handler);
|
|
} else if (window.detachEvent) {
|
|
element.detachEvent("on" + name, handler);
|
|
}
|
|
},
|
|
/**
|
|
* 根据元素的属性进行查找,只要存在此属性即可
|
|
* @param tag 目标标签名
|
|
* @param attr
|
|
* @param root 开始查找的起始节点,可选,默认document
|
|
*/
|
|
getElementsByAttribute: function (tag, attr, root) {
|
|
var parent = root || document,
|
|
result = [];
|
|
var arr = parent.getElementsByTagName(tag),
|
|
a;
|
|
for (var i = 0, l = arr.length; i < l; ++i) {
|
|
a = arr[i];
|
|
if (a.getAttribute(attr) != null) {
|
|
//这个写法...
|
|
result[result.length] = a;
|
|
}
|
|
}
|
|
return result;
|
|
},
|
|
/**
|
|
* 阶乘
|
|
* @param n 操作数 int
|
|
* @return
|
|
*/
|
|
fact: (function () {
|
|
//缓存
|
|
var cache = [1];
|
|
|
|
function factorial(n) {
|
|
var result = cache[n - 1];
|
|
if (result == null) {
|
|
result = 1;
|
|
for (var i = 1; i <= n; ++i) {
|
|
result *= i;
|
|
}
|
|
cache[n - 1] = result;
|
|
}
|
|
return result;
|
|
}
|
|
return factorial;
|
|
})(),
|
|
/**
|
|
* 禁用按键,只有程序员型才会用到
|
|
* @param lis 按钮集合
|
|
* @param mouseOutListener function 鼠标移出时采用哪个监听函数,取决于按钮的位置(上排/下排)
|
|
*/
|
|
disableButtons: function (lis, mouseOutListener) {
|
|
var li;
|
|
for (var i = 0, l = lis.length; i < l; ++i) {
|
|
li = lis[i];
|
|
li.setAttribute("class", "disable-btn");
|
|
cal.removeEvent(li, "click", cal.listeners.keyPressListener);
|
|
cal.removeEvent(li, "mouseout", mouseOutListener);
|
|
cal.removeEvent(li, "mouseover", cal.listeners.mouseHoverListener);
|
|
}
|
|
},
|
|
/**
|
|
* 重新设置按键
|
|
* @param lis 按钮集合
|
|
* @param mouseOutListener function 鼠标移出时采用哪个监听函数,取决于按钮的位置(上排/下排)
|
|
*/
|
|
rebuildButtons: function (lis, mouseOutListener) {
|
|
var li;
|
|
for (var i = 0, l = lis.length; i < l; ++i) {
|
|
li = lis[i];
|
|
li.removeAttribute("class");
|
|
cal.addEvent(li, "click", cal.listeners.keyPressListener);
|
|
cal.addEvent(li, "mouseout", mouseOutListener);
|
|
cal.addEvent(li, "mouseover", cal.listeners.mouseHoverListener);
|
|
}
|
|
}
|
|
};
|
|
return cal;
|
|
})();
|