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("&nbsp;");
                    //程序员型需要把计算结果的四种进制值显示出来
                    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("&nbsp;");
            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;
})();