<commit_before>// replace snake case with camel case
// _([a-zA-Z])([a-zA-Z]*)
// \U$1\L$2

$(document).ready(function(){
    setupTests();
    var autoSwitch = true;
    // If we ever host this, we can enable sharing this way,
    // as long as the code is short enough to fit in a url.
    var urlParams = getURLParameters();
    ///////////////////////////////////////////////////
    // Mips Emulator Setup
    ///////////////////////////////////////////////////
    var me = mipsEmulator({
        debug: false,
        /*
         * Changes the registers visual representation when the mips emulator changes its value
         * @param  {String} regName the register name
         * @param  {String/Number} value    The new value of the register
         * @return {null}
         */
        onRegisterChange: function(regName, value){
            if(regName == "$sp"){
                return setSP(value);
            }
            var reg = $("#" + regName.replace('$', '') + " .regSpacer");
            $(".lastRegChanged").removeClass('lastRegChanged');
            reg.html(value);
            reg.addClass('lastRegChanged');
            // change to correct tab
            var regLetter = regName.replace('$', '').replace(/\s/, '').charAt(0);
            if(autoSwitch) $('#registers a[href="#'+regLetter+'-registers"]').tab('show')
        },
        /*
         * runs when the emulator has pushed past the last line
         * @return {null}
         */
        onFinish: function(){
            addToLog('success', "Emulation complete, returning to line 1");
            me.setLine(1);
            setHighlights({lineRan: lastLineNoRun, nextLine: me.getLineNumber()});
            running = false;
        },
        /*
         * This is run when *the users* program encounters a mips error.
         * @param  {String} message     The error message
         * @param  {Number} lineNumber Which line the error occured on
         * @return {null}
         */
        onError: function(message, lineNumber){
            if(!_.isNumber(lineNumber)) return false;
            editor.markText(
                {line: lineNumber-1, ch: 0},
                {line: lineNumber, ch: 0},
                {title: message, className: 'errorLine', clearOnEnter: true}
            );
        },
        onStackChange: addStackAddress,
        // Set the starting code to be the defualt in the editor.
        startingCode: $("#editor").val()
    });
    var running = false;
    // the active line, is the one whose results are being examined.
    var lastLineNoRun;
    var lastLineAttempted = 0;
    var nextLine;

    ///////////////////////////////////////////////////
    // Code Mirror Setup
    ///////////////////////////////////////////////////

    // Code Mirror lines do not match with displayed line number
    // they start at zero and so are off by  -1
    var editor = CodeMirror.fromTextArea(
        document.getElementById("editor"),{
          lineNumbers: true,
          mode: "text/css",
          matchBrackets: true,
    });

    // When the editor changes, we need to mark it as invalid
    // so it will be reanalyzed upon a step or run
    editor.on('change', markEditorAsInvalid);

    // Keeps track of the last line we ran. To start with we want it
    // to be null becuase we have not run anything yet.
    var lastRunMarker;
    // Keeps track of the next line we will run, we want to set it to the
    // first valid line.
    var nextMarker = editor.markText(
        {line: me.getLineNumber()-1, ch: 0},
        {line: me.getLineNumber(), ch: 0},
        {title: "Next line to be run", className: 'nextLine'}
    );

    ///////////////////////////////////////////////////
    // Event Handlers setup
    ///////////////////////////////////////////////////
    $(".registers-container li").each(setupRegisters);
    $('.regSpacer').on('blur', manualRegistryEdit);
    //$('.regSpacer').on('blur', manualRegistryEditSave);
    $('#codeLoaders button').on('click', loadCustomCode);
    $("#step").click(step);
    $("#goToLineButton").on('click', setLine);
    $("#run").click(run);
    $("#optionShowRelative").change(switchAddressMode);
    $("#autoSwitch").change(function(e){autoSwitch = $(e.target).is(':checked');});
    $("#clearLog").on('click', function(){$("#log").html('')});

    // Functions to respond to events.
    function setLine(){
        var newLine = $("#currentLineInput").val();
        // if(debug) console.log("Setting new line: "+ newLine);
        if(!_.isNumber(me.setLine(Number(newLine)))) console.error("Error setting line: " + newLine);
        // if(debug) console.log("nextLine"+ me.getLineNumber());
        setHighlights({lineRan: null, nextLine: me.getLineNumber()})
        return false;
    };
    function markEditorAsInvalid(){
        me.valid = false;
    };
    function step(){

        // if this code is no longer valid, reanalyze.
        if(!me.valid){
            try{
                mipsAnalyze();
            } catch(e){
                console.error("Error analyzing code: "+JSON.stringify(e));
                addToLog('error', e.message, 1);
            }
        }
        try{
            lastLineAttempted = me.getLineNumber();
            console.log("Attempting line: " + lastLineAttempted);
            lineResult = me.step();
            if(lineResult){
                setHighlights(lineResult);

                //addToLog('info', "ran line successfully", lastLineAttempted);
            }

        } catch(e){
            addToLog('error', e.message, lastLineAttempted);
            running = false;
            //me.setLine(lastLineAttempted + 1);
            //setHighlights();
        }

    };
    function loadCustomCode(e){
        if(!confirm("This will erase what is in the code editor, are you sure?")) return;
        var target = $(e.target);
        var loadTarget = target.attr('load');
        var newContent = $(loadTarget).html();
        newContent = newContent.replace(/^\s+|\s+$/g, '');
        //newContent = newContent.replace(/\n\s+/g, '\n');
        editor.setValue(newContent);
        mipsAnalyze(true);
        me.setLine(1);
        lastLineNoRun = null;
        setHighlights();
    };
    function manualRegistryEditSave(e){
        var newVal = $(e.target).html();
        var target =  $(e.target);
        var regName = target.attr("reg");
        me.setRegisterVal(regName, Number(newVal), false);
    };
    function manualRegistryEdit(e){
        var newVal = $(e.target).html();
        var target =  $(e.target);
        var regName = target.attr("reg");
        newVal = parseInt(newVal);
        try{
            me.setRegisterVal(regName, newVal, false);
        } catch(e) {
            target.html(me.getRegisterVal(regName));
            addToLog('error', e);
        }
    };
    function manualRegistryValidate(e){
        var newVal = $(e.target).html();
        var target =  $(e.target);
        var regName = target.attr("reg");
        if(newVal.search(/[^-\d]/) >= 0){
            target.html(newVal.replace(/[\D\s]/g, ''));
            alert("You cannot enter in characters into registers");
        }
        if(newVal.length > 11){
            if(newVal.length == 12 && newVal.charAt(0) == '-'){
                return true;
            } else {
                target.html(newVal.substring(0, 11));
                alert("The 32 bit integers can only be 11 digits long.");
            }

        }

        return true;
    };
    function setHighlights(lines){
        console.log("Setting highlights")
        lines = lines || {};
        lastLineNoRun = lines.lineRan || lastLineNoRun || null;
        nextLine = lines.nextLine || me.getLineNumber();
        // if(debug) console.log("Active line: " + lastLineNoRun);
        // if(debug) console.log("Next line: " + nextLine);
        $("#currentLineInput").val(nextLine);
        if(lastRunMarker) lastRunMarker.clear();
        if(lastLineNoRun)
            lastRunMarker = editor.markText(
                {line: lastLineNoRun-1, ch: 0},
                {line: lastLineNoRun, ch: 0},
                {title: "last line ran", className: 'lastLineNoRun', clearOnEnter: true}
            );
        if(nextMarker) nextMarker.clear();
        nextMarker = editor.markText(
            {line: nextLine-1, ch: 0},
            {line: nextLine, ch: 0},
            {title: "Next line to be run", className: 'nextLine', clearOnEnter: true}
        );

    };
    function setupRegisters(index){
        var reg = $(this);
        var regName = reg.attr('id');
        reg.html(
            ""+regName +": "
            + ""
            + me.getRegisterVal('$' + regName)
            + ""
        );
    }
    function run(){
        // if this code is no longer valid, reanalyze.
        if(!me.valid){
            editor.save();
            me.setCode($("#editor").val());
            me.valid = true;
        }
        running = true;
        while(running) step();

    };
    function mipsAnalyze(goBackToLineOne){
        editor.save();
        me.setCode($("#editor").val());
        if(goBackToLineOne) me.setLine(1);
        me.valid = true;
    };
    function getCodeAsString(){
        editor.save();
        return $("#editor").val();
    };
    function unsignInt(num){
        return (num << 31) >>> 0;
    };
    function setSP(address){
        addStackAddress(address, '', false);
        $(".glyphicon-arrow-right").removeClass("glyphicon-arrow-right");
        $("#stackEntry-" + address + " .glyphicon").addClass("glyphicon-arrow-right lastRegChanged");
        if(autoSwitch) $('#registers a[href="#stack-container-div"]').tab('show');
            };
    var colorizeAddrBG = false;
    function addStackAddress(address, val, visualize){
        if(!val || val == '') val = me.stack.getByte(address);
        if(typeof visualize == 'undefined')
            visualize = true;
        console.log("address: " + address + "\nStackLow: " + stackLow + "\nVal: " + val );
        showAddReal = '';
            showAddRelative = 'style="display: none"';
        if(showRelative){
            showAddReal = 'style="display: none"';
            showAddRelative = '';
        }
        while(address <= stackLow){
            var bgColorClass = '';
            if(colorizeAddrBG) bgColorClass = 'lightGreyBG';
            colorizeAddrBG = !colorizeAddrBG;
            $("#stackRep").prepend(
                "
"
                + "&nbsp"
                    + ""
                        + ""
                            + stackLow + ": "
                        + ""
                        + ""
                            + (stackLow - stackEnd) + ": "
                        + ""
                        + ""+
                            + me.stack.getByte(stackLow)
                        +""
                    + ""
                + "
"
            );
            stackLow--;
        }
        console.log("stack Change: " + address + " - " + val);

        $("#stackVal-"+address).html(val);
        if(visualize){
            if(autoSwitch) $('#registers a[href="#stack-container-div"]').tab('show');
            $(".lastRegChanged").removeClass('lastRegChanged');
            $("#stackVal-"+address).addClass('lastRegChanged');
        }
    };
    var stackLow = me.stack.pointerToBottomOfStack()-1;
    var stackEnd = me.stack.pointerToBottomOfStack();
    addStackAddress(stackLow, me.stack.getByte(stackLow), false);
    //setSP(stackEnd);
    function setupTests(){
        //

        // <button type="button" load="#additionDoubler" class="btn btn-default">Addition Doubler</button>
        $.each(examples, function(index, func){
            index = index.replace('Example', '');
            indexNice = index.replace(/([A-Z])/g, " \$1");
            indexNice = indexNice.charAt(0).toUpperCase() + indexNice.slice(1);;

            $("#exampleHolder").append(
                "
"
                + func().join('\n')
                + "
"
            );
            $("#codeLoaders").append(
                '<button type="button" load="#'
                + index + '" class="btn btn-default">'
                + indexNice + '</button>'
            );
        });
    };
    var showRelative;
    function switchAddressMode(e){
        showRelative = $(e.target).is(':checked');
        if(showRelative){
            $('.stackAddrRelative').show();
            $('.stackAddrReal').hide();
        } else {
            $('.stackAddrRelative').hide();
            $('.stackAddrReal').show();
        }
    }
    function addToLog(type, message, line_no){
        if(type == 'error') type = 'danger';
        if(line_no){
            message = "Line " + line_no + ": " + message;
            if(type == 'danger'){
                editor.markText(
                    {line: line_no-1, ch: 0},
                    {line: line_no, ch: 0},
                    {title: message, className: 'errorLine', clearOnEnter: true}
                );
            }
        }
        $("#log").prepend(
            '
'+message+'
'
        );
        if(autoSwitch) $('#registers a[href="#logWrapper"]').tab('show');

    }

});<commit_msg>added infinite loop catcher<commit_after>// replace snake case with camel case
// _([a-zA-Z])([a-zA-Z]*)
// \U$1\L$2

$(document).ready(function(){
    setupTests();
    var autoSwitch = true;
    // If we ever host this, we can enable sharing this way,
    // as long as the code is short enough to fit in a url.
    var urlParams = getURLParameters();
    ///////////////////////////////////////////////////
    // Mips Emulator Setup
    ///////////////////////////////////////////////////
    var me = mipsEmulator({
        debug: false,
        /*
         * Changes the registers visual representation when the mips emulator changes its value
         * @param  {String} regName the register name
         * @param  {String/Number} value    The new value of the register
         * @return {null}
         */
        onRegisterChange: function(regName, value){
            if(regName == "$sp"){
                return setSP(value);
            }
            var reg = $("#" + regName.replace('$', '') + " .regSpacer");
            $(".lastRegChanged").removeClass('lastRegChanged');
            reg.html(value);
            reg.addClass('lastRegChanged');
            // change to correct tab
            var regLetter = regName.replace('$', '').replace(/\s/, '').charAt(0);
            if(autoSwitch) $('#registers a[href="#'+regLetter+'-registers"]').tab('show')
        },
        /*
         * runs when the emulator has pushed past the last line
         * @return {null}
         */
        onFinish: function(){
            addToLog('success', "Emulation complete, returning to line 1");
            me.setLine(1);
            setHighlights({lineRan: lastLineNoRun, nextLine: me.getLineNumber()});
            running = false;
        },
        /*
         * This is run when *the users* program encounters a mips error.
         * @param  {String} message     The error message
         * @param  {Number} lineNumber Which line the error occured on
         * @return {null}
         */
        onError: function(message, lineNumber){
            if(!_.isNumber(lineNumber)) return false;
            editor.markText(
                {line: lineNumber-1, ch: 0},
                {line: lineNumber, ch: 0},
                {title: message, className: 'errorLine', clearOnEnter: true}
            );
        },
        onStackChange: addStackAddress,
        // Set the starting code to be the defualt in the editor.
        startingCode: $("#editor").val()
    });
    var running = false;
    // the active line, is the one whose results are being examined.
    var lastLineNoRun;
    var lastLineAttempted = 0;
    var nextLine;
    var linesOfCode = 0;

    ///////////////////////////////////////////////////
    // Code Mirror Setup
    ///////////////////////////////////////////////////

    // Code Mirror lines do not match with displayed line number
    // they start at zero and so are off by  -1
    var editor = CodeMirror.fromTextArea(
        document.getElementById("editor"),{
          lineNumbers: true,
          mode: "text/css",
          matchBrackets: true,
    });

    // When the editor changes, we need to mark it as invalid
    // so it will be reanalyzed upon a step or run
    editor.on('change', markEditorAsInvalid);

    // Keeps track of the last line we ran. To start with we want it
    // to be null becuase we have not run anything yet.
    var lastRunMarker;
    // Keeps track of the next line we will run, we want to set it to the
    // first valid line.
    var nextMarker = editor.markText(
        {line: me.getLineNumber()-1, ch: 0},
        {line: me.getLineNumber(), ch: 0},
        {title: "Next line to be run", className: 'nextLine'}
    );

    ///////////////////////////////////////////////////
    // Event Handlers setup
    ///////////////////////////////////////////////////
    $(".registers-container li").each(setupRegisters);
    $('.regSpacer').on('blur', manualRegistryEdit);
    //$('.regSpacer').on('blur', manualRegistryEditSave);
    $('#codeLoaders button').on('click', loadCustomCode);
    $("#step").click(step);
    $("#goToLineButton").on('click', setLine);
    $("#run").click(run);
    $("#optionShowRelative").change(switchAddressMode);
    $("#autoSwitch").change(function(e){autoSwitch = $(e.target).is(':checked');});
    $("#clearLog").on('click', function(){$("#log").html('')});

    // Functions to respond to events.
    function setLine(){
        var newLine = $("#currentLineInput").val();
        // if(debug) console.log("Setting new line: "+ newLine);
        if(!_.isNumber(me.setLine(Number(newLine)))) console.error("Error setting line: " + newLine);
        // if(debug) console.log("nextLine"+ me.getLineNumber());
        setHighlights({lineRan: null, nextLine: me.getLineNumber()})
        return false;
    };
    function markEditorAsInvalid(){
        me.valid = false;
    };
    function step(){

        // if this code is no longer valid, reanalyze.
        if(!me.valid){
            try{
                mipsAnalyze();
            } catch(e){
                console.error("Error analyzing code: "+JSON.stringify(e));
                addToLog('error', e.message, 1);
            }
        }
        try{
            lastLineAttempted = me.getLineNumber();
            console.log("Attempting line: " + lastLineAttempted);
            lineResult = me.step();
            if(lineResult){
                setHighlights(lineResult);

                //addToLog('info', "ran line successfully", lastLineAttempted);
            }

        } catch(e){
            addToLog('error', e.message, lastLineAttempted);
            running = false;
            //me.setLine(lastLineAttempted + 1);
            //setHighlights();
        }

    };
    function loadCustomCode(e){
        if(!confirm("This will erase what is in the code editor, are you sure?")) return;
        var target = $(e.target);
        var loadTarget = target.attr('load');
        var newContent = $(loadTarget).html();
        newContent = newContent.replace(/^\s+|\s+$/g, '');
        //newContent = newContent.replace(/\n\s+/g, '\n');
        editor.setValue(newContent);
        mipsAnalyze(true);
        me.setLine(1);
        lastLineNoRun = null;
        setHighlights();
    };
    function manualRegistryEditSave(e){
        var newVal = $(e.target).html();
        var target =  $(e.target);
        var regName = target.attr("reg");
        me.setRegisterVal(regName, Number(newVal), false);
    };
    function manualRegistryEdit(e){
        var newVal = $(e.target).html();
        var target =  $(e.target);
        var regName = target.attr("reg");
        newVal = parseInt(newVal);
        try{
            me.setRegisterVal(regName, newVal, false);
        } catch(e) {
            target.html(me.getRegisterVal(regName));
            addToLog('error', e);
        }
    };
    function manualRegistryValidate(e){
        var newVal = $(e.target).html();
        var target =  $(e.target);
        var regName = target.attr("reg");
        if(newVal.search(/[^-\d]/) >= 0){
            target.html(newVal.replace(/[\D\s]/g, ''));
            alert("You cannot enter in characters into registers");
        }
        if(newVal.length > 11){
            if(newVal.length == 12 && newVal.charAt(0) == '-'){
                return true;
            } else {
                target.html(newVal.substring(0, 11));
                alert("The 32 bit integers can only be 11 digits long.");
            }

        }

        return true;
    };
    function setHighlights(lines){
        console.log("Setting highlights")
        lines = lines || {};
        lastLineNoRun = lines.lineRan || lastLineNoRun || null;
        nextLine = lines.nextLine || me.getLineNumber();
        // if(debug) console.log("Active line: " + lastLineNoRun);
        // if(debug) console.log("Next line: " + nextLine);
        $("#currentLineInput").val(nextLine);
        if(lastRunMarker) lastRunMarker.clear();
        if(lastLineNoRun)
            lastRunMarker = editor.markText(
                {line: lastLineNoRun-1, ch: 0},
                {line: lastLineNoRun, ch: 0},
                {title: "last line ran", className: 'lastLineNoRun', clearOnEnter: true}
            );
        if(nextMarker) nextMarker.clear();
        nextMarker = editor.markText(
            {line: nextLine-1, ch: 0},
            {line: nextLine, ch: 0},
            {title: "Next line to be run", className: 'nextLine', clearOnEnter: true}
        );

    };
    function setupRegisters(index){
        var reg = $(this);
        var regName = reg.attr('id');
        reg.html(
            ""+regName +": "
            + ""
            + me.getRegisterVal('$' + regName)
            + ""
        );
    }
    function run(){
        // if this code is no longer valid, reanalyze.
        if(!me.valid){
            editor.save();
            me.setCode($("#editor").val());
            me.valid = true;
        }
        running = true;
        var lineRanThisRun = 0;
        var notInfinite = false;
        while(running){

            step();
            lineRanThisRun++;
            if(lineRanThisRun > 10 * linesOfCode && !notInfinite)
                if(confirm(
                    "Code has run "
                    + lineRanThisRun
                    + " lines, are you stuck in an infinite loop? (press OK to stop executing)"))
                    running = false;
                else
                    notInfinite = true;
        }

    };
    function mipsAnalyze(goBackToLineOne){
        editor.save();
        linesOfCode = editor.lineCount();
        me.setCode($("#editor").val());
        if(goBackToLineOne) me.setLine(1);
        me.valid = true;
    };
    function getCodeAsString(){
        editor.save();
        return $("#editor").val();
    };
    function unsignInt(num){
        return (num << 31) >>> 0;
    };
    function setSP(address){
        addStackAddress(address, '', false);
        $(".glyphicon-arrow-right").removeClass("glyphicon-arrow-right");
        $("#stackEntry-" + address + " .glyphicon").addClass("glyphicon-arrow-right lastRegChanged");
        if(autoSwitch) $('#registers a[href="#stack-container-div"]').tab('show');
            };
    var colorizeAddrBG = false;
    function addStackAddress(address, val, visualize){
        if(!val || val == '') val = me.stack.getByte(address);
        if(typeof visualize == 'undefined')
            visualize = true;
        console.log("address: " + address + "\nStackLow: " + stackLow + "\nVal: " + val );
        showAddReal = '';
            showAddRelative = 'style="display: none"';
        if(showRelative){
            showAddReal = 'style="display: none"';
            showAddRelative = '';
        }
        while(address <= stackLow){
            var bgColorClass = '';
            if(colorizeAddrBG) bgColorClass = 'lightGreyBG';
            colorizeAddrBG = !colorizeAddrBG;
            $("#stackRep").prepend(
                "
"
                + "&nbsp"
                    + ""
                        + ""
                            + stackLow + ": "
                        + ""
                        + ""
                            + (stackLow - stackEnd) + ": "
                        + ""
                        + ""+
                            + me.stack.getByte(stackLow)
                        +""
                    + ""
                + "
"
            );
            stackLow--;
        }
        console.log("stack Change: " + address + " - " + val);

        $("#stackVal-"+address).html(val);
        if(visualize){
            if(autoSwitch) $('#registers a[href="#stack-container-div"]').tab('show');
            $(".lastRegChanged").removeClass('lastRegChanged');
            $("#stackVal-"+address).addClass('lastRegChanged');
        }
    };
    var stackLow = me.stack.pointerToBottomOfStack()-1;
    var stackEnd = me.stack.pointerToBottomOfStack();
    addStackAddress(stackLow, me.stack.getByte(stackLow), false);
    //setSP(stackEnd);
    function setupTests(){
        //

        // <button type="button" load="#additionDoubler" class="btn btn-default">Addition Doubler</button>
        $.each(examples, function(index, func){
            index = index.replace('Example', '');
            indexNice = index.replace(/([A-Z])/g, " \$1");
            indexNice = indexNice.charAt(0).toUpperCase() + indexNice.slice(1);;

            $("#exampleHolder").append(
                "
"
                + func().join('\n')
                + "
"
            );
            $("#codeLoaders").append(
                '<button type="button" load="#'
                + index + '" class="btn btn-default">'
                + indexNice + '</button>'
            );
        });
    };
    var showRelative;
    function switchAddressMode(e){
        showRelative = $(e.target).is(':checked');
        if(showRelative){
            $('.stackAddrRelative').show();
            $('.stackAddrReal').hide();
        } else {
            $('.stackAddrRelative').hide();
            $('.stackAddrReal').show();
        }
    }
    function addToLog(type, message, line_no){
        if(type == 'error') type = 'danger';
        if(line_no){
            message = "Line " + line_no + ": " + message;
            if(type == 'danger'){
                editor.markText(
                    {line: line_no-1, ch: 0},
                    {line: line_no, ch: 0},
                    {title: message, className: 'errorLine', clearOnEnter: true}
                );
            }
        }
        $("#log").prepend(
            '
'+message+'
'
        );
        if(autoSwitch) $('#registers a[href="#logWrapper"]').tab('show');

    }

});<|endoftext|>


<commit_before>(function() {

  var callbacksCount = 1;
  var callbacks = {};

  var isArray = Array.isArray || function(obj) {
    return obj instanceof Array;
  };

  var extend = function(obj, source) {
    if (!source) return obj;

    for (var prop in source) {
      if (source[prop] !== undefined)
        obj[prop] = source[prop];
    }

    return obj;
  };

  var noop = function() {}

  var callbackDelegate = function(callback, args) {
    if (typeof callback === 'function') {
      callback.apply(null, args)
    }
  };

  var gaCallbackDelegate = function(success, error, data, defaultMsg) {
    if (!data || !data.status) {
      callbackDelegate(error, [data.message || defaultMsg]);
    } else {
      callbackDelegate(success, [data.message || '']);
    }
  };

  var Efte = {

    send_message: function(method, args, callback) {
      var hasCallback = callback && typeof callback == 'function';

      var callbackId = hasCallback ? callbacksCount++ : 0;

      if (hasCallback) {
        callbacks[callbackId] = callback;
      }
      for(var index in callbacks) {
        console.log('---- send_message ' + index + ' ' + callbacks[index]);
      }

      args = args || {};

      args['callbackId'] = callbackId;

      // args must an dictionary
      if (Object.prototype.toString.call(args) !== '[object Object]') {
        throw new Error('Efte.send_message: args must be an object');
      }

      args = JSON.stringify(args);

      var ifr = document.createElement('iframe');
      ifr.style.display = 'none';
      document.body.appendChild(ifr);
      ifr.contentWindow.location.href = 'js://_?method=' + method + '&args=' + encodeURIComponent(args) + '&callbackId=' + callbackId + '&stamp=' + (new Date()).getTime();
      // ifr.parentNode.removeChild(ifr);
      setTimeout(function() {
        ifr.parentNode.removeChild(ifr);
      }, 0);

      return callbackId;
    },

    callback: function(callbackId, retValue) {
      try {
        var callback = callbacks[callbackId];
        console.log('---- callback id: ' + callbackId + ' callback: ' + callback);
        if (!callback) return;
        callback.apply(null, [retValue]);
        // delete callbacks[callbackId];
      } catch (e) {
        alert(e);
      }
    },

    ajax: function(opts) {
      var success = opts.success || noop;
      var error = opts.error || noop;

      // null data lead native error
      if (!opts.data) {
        delete opts.data;
      }

      var contentTypeToDataType = function(contentType) {
        var jsonType = 'application/json';
        var htmlType = 'text/html';
        var xmlTypeRE = /^(?:text|application)\/xml/i;

        if (contentType) {
          contentType = contentType.split(';', 2)[0];
        }

        return contentType && (contentType == jsonType ? 'json' : contentType == htmlType ? 'html' : xmlTypeRE.test(contentType) && 'xml') || 'text';
      }

      var callbackID = Efte.send_message('ajax', opts, function(xhr) {
        var errMsg = '';
        var dataType = contentTypeToDataType(xhr.dataType);
        var result = xhr.responseText;

        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && opts.url.indexOf('efte:') == 0)) {
          try {
            if (dataType == 'json') {
              result = /^\s*$/.test(result) ? null : JSON.parse(result);
            }
          } catch (e) {
            error(xhr.status, 'parsererror', xhr);
            delete callbacks[callbackID];
            return;
          }

          success(result);
          delete callbacks[callbackID];
        } else {
          error(xhr.status, xhr.message);
          delete callbacks[callbackID];
        }
      });
      return {
        abort: function() {
          delete callbacks[callbackID];
        },

        cancel: function() {
          // deprecated, use abort instead
          delete callbacks[callbackID];
        }
      };
    },

    action: {
      reloadPage: function() {
        if (Efte.getPlatform() === 'android') {
            Efte.send_message('reloadPage');
        }
        else {
          location.reload();
        }
      },

      get: function(callback) {
        var cid = Efte.send_message('actionGetQuery', {}, function(query) {
          if (typeof callback != 'function') return;
          callback(query);
          delete callbacks[cid];
        });
      },
      /**
       * 打开新窗口
       * @param  {[String]} unit     [description]
       * @param  {[String]} path     [description]
       * @param  {[Object]} query    [description]
       * @param  {[Boolean]} modal    [description]
       * @param  {[Boolean]} animated [description]
       */
      open: function(unit, path, query, modal, animated) {
        var opt = {
          unit: unit,
          path: path
        };

        if (Object.prototype.toString.call(query) === '[object Object]') {
          opt.query = query;
        }

        if (modal != null) {
          opt.modal = !!modal;
        }

        opt.animated = animated != null ? !!animated : Efte.getPlatform() !== 'android';

        var cid = Efte.send_message('actionOpen', opt, function() {
          delete callbacks[cid];
        });
      },

      openUrl: function(url, modal, animated) {
        Efte.send_message('openUrl', {
          url: url,
          modal: modal,
          animated: animated
        });
      },

      willBack: function() {
        return true;
      },

      back: function(animated) {
        if (Efte.action.willBack() === false) {
          return;
        }

        if (animated === undefined) {
          animated = true;
        }
        var cid = Efte.send_message('actionBack', {
          animated: animated
        }, function() {
          delete callbacks[cid];
        });
      },
      dismiss: function(animated) {
        if (animated === undefined) {
          animated = true;
        }
        var cid = Efte.send_message('actionDismiss', {
          animated: animated
        }, function() {
          delete callbacks[cid];
        });
      }
    },

    getEnv: function(callback) {
      var cid = Efte.send_message('getEnv', {}, function(env) {
        window.AppEnv = env;
        callback(env && env.value);
        delete callbacks[cid];
      });
    },

    getPlatform: function() {
      var userAgent = window.navigator.userAgent.toLowerCase();
      if(userAgent.indexOf('efte-for-android') != -1) {
        return 'android';
      }
      if(userAgent.indexOf('efte-for-ios') != -1) {
        return 'ios';
      }
      return '';
    },

    enableRefresh: function() {
      Efte.send_message('enableRefresh');
    },

    startRefresh: function() {
      Efte.stopRefresh();
    },

    stopRefresh: function() {
      Efte.send_message('stopRefresh');
    },

    genUUID: function(id) {
      // buggy
      id = id || AppEnv.dpid;

      // MD5?
      var requid = CryptoJS.MD5(id + (new Date().getTime()) + (Math.random()));
      // console.log(requid.toString());
      return requid.toString();
    },
    setTitle: function(title) {
      var cid = Efte.send_message('setTitle', {
        title: title
      }, function() {
        delete callbacks[cid];
      });
    },
    takePhoto: function(callback) {
      var cid = Efte.send_message('imagePicker', {}, function(result) {
        typeof callback == 'function' && callback(result);
        delete callbacks[cid];
      });
    },
    takePhotoByName: function(name, callback, fail) {
      var cid = Efte.send_message('imagePickerWithName', {
        name: name
      }, function(result) {
        if (result.image) {
          typeof callback == 'function' && callback(result.image);
        } else {
          typeof fail == 'function' && fail(result.message);
        }
        delete callbacks[cid];
      })
    },

    // 设置导航栏按钮
    /**
     * [{
     *  title: 'Foo',
     *  action: function () {}
     * }]
     */
    setBarButtons: function(buttons) {
      buttons = isArray(buttons) ? buttons : [buttons];
      var actionFns = [];
      buttons.forEach(function(button) {
        actionFns.push(button.action || noop);
      });
      Efte.send_message('setBarButtons', {
        buttons: buttons
      }, function(result) {
        actionFns[result.index]();
      });
    },

    /* notification publish & subscribe */
    publish: function(name, value) {
      Efte.send_message('publish', {
        name: name,
        value: value
      });
    },

    /* callback(publisher's value); */
    subscribe: function(name, callback) {
      var cid = Efte.send_message('subscribe', {
        name: name
      }, function(result) {
        (typeof callback == 'function') && callback(result.value);
      });
      return {
        unsubscribe: function() {
          delete callbacks[cid];
        }
      }
    },

    multilevelFromResource: function(unit, path, values, callback) {
      Efte.send_message('multilevel', {
        unit: unit,
        path: path,
        values: values
      }, function(result) {
        (typeof callback == 'function') && callback(result.value);
      })
    },

    multilevel: function(options, values, callback) {
      Efte.send_message('multilevel', {
        options: options,
        values: values
      }, function(result) {
        (typeof callback == 'function') && callback(result.value);
      })
    },
    /**
     * datePicker
     * @options  {Object}   options    [description]
     *
     *  type: {String} date|time|datetime(default)
     *  default: {String} 2014-11-11 12:12:12(default) | 2014-11-11 | 12:12:12
     *  ...
     *
     * @callback  {Function} callback [description]
     *
     */

     // deprecated 因为参数过多，现在使用date 这个api
    datePicker: function(type, defaultDate, callback) {
      Efte.send_message('datepicker', {
        'type': type || '',
        'default': defaultDate || ''
      }, function(result) {
        (typeof callback == 'function') && callback(result.value);
      });
    },

    /*
      type: {String} date|time|datetime(default)
      default: {String} 2014-11-11 12:12:12(default) | 2014-11-11 | 12:12:12
      minuteInterval: 分钟间隔，必须能被60整除，例如：1,10,15,30
      minDate: 最小的有效日期，以1970年开始的毫秒数
      maxDate: 最大的有效日期，以1970年开始的毫秒数
    */
    date: function(options, callback) {
      Efte.send_message('datepicker', options, function(result) {
        (typeof callback == 'function') && callback(result.value);
      });
    },

    logout: function() {
      Efte.send_message('logout');
    },

    geo: {
      getCurrentPosition: function(success, error) {
        Efte.send_message('location', {}, function(position) {
          if (!position || !position.status) {
            error(position.message || '定位失败');
          } else {
            success(position);
          }
        });
      }
    },
    showPhoto: function(data, delCallback) {
      var cid = Efte.send_message('showPhoto', data, function(retValue) {
        if (retValue && retValue.action && retValue.action == 'del') {
          delCallback();
        }
        delete callbacks[cid];
      });
    },
    ga: {
      sendEvent: function(category, action, label, value, success, error) {
        var cid = Efte.send_message('ga_sendEvent', {
          category: category,
          action: action,
          label: label,
          value: value
        }, function(data) {
          gaCallbackDelegate(success, error, data, 'ga send event 失败');
          delete callbacks[cid];
        });
      },
      sendView: function(viewName, success, error) {
        var cid = Efte.send_message('ga_sendView', {
          viewName: viewName
        }, function(data) {
          gaCallbackDelegate(success, error, data, 'ga send view 失败');
          delete callbacks[cid];
        });
      },
      sendTiming: function(category, interval, name, label, success, error) {
        var cid = Efte.send_message('ga_sendTiming', {
          category: category,
          interval: interval,
          name: name,
          label: label
        }, function(data) {
          gaCallbackDelegate(success, error, data, 'ga send timing 失败');
          delete callbacks[cid];
        });
      },
      sendException: function(description, fatal, success, error) {
        var cid = Efte.send_message('ga_sendException', {
          description: description,
          fatal: fatal
        }, function(data) {
          gaCallbackDelegate(success, error, data, 'ga send exception 失败');
          delete callbacks[cid];
        });
      },
      setCustomDimension: function(dimentionIndex, value, success, error) {
        var cid = Efte.send_message('ga_setCustomDimension', {
          dimentionIndex: dimentionIndex,
          value: value
        }, function(data) {
          gaCallbackDelegate(success, error, data, 'ga set custom dimension 失败');
          delete callbacks[cid];
        });
      },
      setCustomMetric: function(metricIndex, value, success, error) {
        var cid = Efte.send_message('ga_setCustomMetric', {
          metricIndex: metricIndex,
          value: value
        }, function(data) {
          gaCallbackDelegate(success, error, data, 'ga set custom metric 失败');
          delete callbacks[cid];
        });
      }
    },
    cache: {
      save: function(key, value, opts) {
        opts = opts || {};
        var params = {
          key: key,
          value: value,
          isTemp: !!opts.isTemp
        };

        if (opts.expiration != null) {
          params.expiration = opts.expiration;
        }

        var cid = Efte.send_message('cacheSave', params, function(data) {
          delete callbacks[cid];
          if (!data || !data.status) {
            callbackDelegate(opts.error, [data.message || 'cache保存失败']);
            return;
          }
          callbackDelegate(opts.success, [data.message || 'cache保存成功']);
        });
      },
      load: function(key, opts) {
        opts = opts || {};
        var cid = Efte.send_message('cacheLoad', {
          key: key
        }, function(data) {
          delete callbacks[cid];
          if (!data || !data.status) {
            callbackDelegate(opts.error, [data.message || 'cache读取失败']);
            return;
          }
          callbackDelegate(opts.success, [data]);
        });
      },
      remove: function(key, opts) {
        opts = opts || {};
        var cid = Efte.send_message('cacheDelete', {
          key: key
        }, function(data) {
          delete callbacks[cid];
          if (!data || !data.status) {
            callbackDelegate(opts.error, [data.message || 'cache删除失败']);
            return;
          }
          callbackDelegate(opts.success, [data]);
        });
      },
      removeAll: function(opts) {
        opts = opts || {};
        var cid = Efte.send_message('cacheClear', {}, function(data) {
          delete callbacks[cid];
          if (!data || !data.status) {
            callbackDelegate(opts.error, [data.message || 'cache清除失败']);
            return;
          }
          callbackDelegate(opts.success, [data]);
        });
      }
    },
    push: {
      getInfo: function(success, error) {
        console.log('---- in efte.push.getInfo');
        var cid = Efte.send_message('push_getInfo', {}, function(data) {
          console.log('---- in efte.push.getInfo callback: ' + JSON.stringify(data));
          delete callbacks[cid];
          if (!data || !data.status) {
            callbackDelegate(error, [data]);
            return;
          }
          callbackDelegate(success, [data]);
        })
      },
      getExtra: function(success, error) {
        var cid = Efte.send_message('push_getExtra', {}, function(data) {
          delete callbacks[cid];
          if (!data || !data.status || !data.extra) {
            callbackDelegate(error, [data]);
            return;
          }
          callbackDelegate(success, [data.extra]);
        });
      }
    }
  };

  try {
    module.exports = Efte;
  } catch (e) {
    window.Efte = Efte;
  }

})();
<commit_msg>remove debug messages<commit_after>(function() {

  var callbacksCount = 1;
  var callbacks = {};

  var isArray = Array.isArray || function(obj) {
    return obj instanceof Array;
  };

  var extend = function(obj, source) {
    if (!source) return obj;

    for (var prop in source) {
      if (source[prop] !== undefined)
        obj[prop] = source[prop];
    }

    return obj;
  };

  var noop = function() {}

  var callbackDelegate = function(callback, args) {
    if (typeof callback === 'function') {
      callback.apply(null, args)
    }
  };

  var gaCallbackDelegate = function(success, error, data, defaultMsg) {
    if (!data || !data.status) {
      callbackDelegate(error, [data.message || defaultMsg]);
    } else {
      callbackDelegate(success, [data.message || '']);
    }
  };

  var Efte = {

    send_message: function(method, args, callback) {
      var hasCallback = callback && typeof callback == 'function';

      var callbackId = hasCallback ? callbacksCount++ : 0;

      if (hasCallback) {
        callbacks[callbackId] = callback;
      }

      args = args || {};

      args['callbackId'] = callbackId;

      // args must an dictionary
      if (Object.prototype.toString.call(args) !== '[object Object]') {
        throw new Error('Efte.send_message: args must be an object');
      }

      args = JSON.stringify(args);

      var ifr = document.createElement('iframe');
      ifr.style.display = 'none';
      document.body.appendChild(ifr);
      ifr.contentWindow.location.href = 'js://_?method=' + method + '&args=' + encodeURIComponent(args) + '&callbackId=' + callbackId + '&stamp=' + (new Date()).getTime();
      // ifr.parentNode.removeChild(ifr);
      setTimeout(function() {
        ifr.parentNode.removeChild(ifr);
      }, 0);

      return callbackId;
    },

    callback: function(callbackId, retValue) {
      try {
        var callback = callbacks[callbackId];
        if (!callback) return;
        callback.apply(null, [retValue]);
        // delete callbacks[callbackId];
      } catch (e) {
        alert(e);
      }
    },

    ajax: function(opts) {
      var success = opts.success || noop;
      var error = opts.error || noop;

      // null data lead native error
      if (!opts.data) {
        delete opts.data;
      }

      var contentTypeToDataType = function(contentType) {
        var jsonType = 'application/json';
        var htmlType = 'text/html';
        var xmlTypeRE = /^(?:text|application)\/xml/i;

        if (contentType) {
          contentType = contentType.split(';', 2)[0];
        }

        return contentType && (contentType == jsonType ? 'json' : contentType == htmlType ? 'html' : xmlTypeRE.test(contentType) && 'xml') || 'text';
      }

      var callbackID = Efte.send_message('ajax', opts, function(xhr) {
        var errMsg = '';
        var dataType = contentTypeToDataType(xhr.dataType);
        var result = xhr.responseText;

        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && opts.url.indexOf('efte:') == 0)) {
          try {
            if (dataType == 'json') {
              result = /^\s*$/.test(result) ? null : JSON.parse(result);
            }
          } catch (e) {
            error(xhr.status, 'parsererror', xhr);
            delete callbacks[callbackID];
            return;
          }

          success(result);
          delete callbacks[callbackID];
        } else {
          error(xhr.status, xhr.message);
          delete callbacks[callbackID];
        }
      });
      return {
        abort: function() {
          delete callbacks[callbackID];
        },

        cancel: function() {
          // deprecated, use abort instead
          delete callbacks[callbackID];
        }
      };
    },

    action: {
      reloadPage: function() {
        if (Efte.getPlatform() === 'android') {
            Efte.send_message('reloadPage');
        }
        else {
          location.reload();
        }
      },

      get: function(callback) {
        var cid = Efte.send_message('actionGetQuery', {}, function(query) {
          if (typeof callback != 'function') return;
          callback(query);
          delete callbacks[cid];
        });
      },
      /**
       * 打开新窗口
       * @param  {[String]} unit     [description]
       * @param  {[String]} path     [description]
       * @param  {[Object]} query    [description]
       * @param  {[Boolean]} modal    [description]
       * @param  {[Boolean]} animated [description]
       */
      open: function(unit, path, query, modal, animated) {
        var opt = {
          unit: unit,
          path: path
        };

        if (Object.prototype.toString.call(query) === '[object Object]') {
          opt.query = query;
        }

        if (modal != null) {
          opt.modal = !!modal;
        }

        opt.animated = animated != null ? !!animated : Efte.getPlatform() !== 'android';

        var cid = Efte.send_message('actionOpen', opt, function() {
          delete callbacks[cid];
        });
      },

      openUrl: function(url, modal, animated) {
        Efte.send_message('openUrl', {
          url: url,
          modal: modal,
          animated: animated
        });
      },

      willBack: function() {
        return true;
      },

      back: function(animated) {
        if (Efte.action.willBack() === false) {
          return;
        }

        if (animated === undefined) {
          animated = true;
        }
        var cid = Efte.send_message('actionBack', {
          animated: animated
        }, function() {
          delete callbacks[cid];
        });
      },
      dismiss: function(animated) {
        if (animated === undefined) {
          animated = true;
        }
        var cid = Efte.send_message('actionDismiss', {
          animated: animated
        }, function() {
          delete callbacks[cid];
        });
      }
    },

    getEnv: function(callback) {
      var cid = Efte.send_message('getEnv', {}, function(env) {
        window.AppEnv = env;
        callback(env && env.value);
        delete callbacks[cid];
      });
    },

    getPlatform: function() {
      var userAgent = window.navigator.userAgent.toLowerCase();
      if(userAgent.indexOf('efte-for-android') != -1) {
        return 'android';
      }
      if(userAgent.indexOf('efte-for-ios') != -1) {
        return 'ios';
      }
      return '';
    },

    enableRefresh: function() {
      Efte.send_message('enableRefresh');
    },

    startRefresh: function() {
      Efte.stopRefresh();
    },

    stopRefresh: function() {
      Efte.send_message('stopRefresh');
    },

    genUUID: function(id) {
      // buggy
      id = id || AppEnv.dpid;

      // MD5?
      var requid = CryptoJS.MD5(id + (new Date().getTime()) + (Math.random()));
      // console.log(requid.toString());
      return requid.toString();
    },
    setTitle: function(title) {
      var cid = Efte.send_message('setTitle', {
        title: title
      }, function() {
        delete callbacks[cid];
      });
    },
    takePhoto: function(callback) {
      var cid = Efte.send_message('imagePicker', {}, function(result) {
        typeof callback == 'function' && callback(result);
        delete callbacks[cid];
      });
    },
    takePhotoByName: function(name, callback, fail) {
      var cid = Efte.send_message('imagePickerWithName', {
        name: name
      }, function(result) {
        if (result.image) {
          typeof callback == 'function' && callback(result.image);
        } else {
          typeof fail == 'function' && fail(result.message);
        }
        delete callbacks[cid];
      })
    },

    // 设置导航栏按钮
    /**
     * [{
     *  title: 'Foo',
     *  action: function () {}
     * }]
     */
    setBarButtons: function(buttons) {
      buttons = isArray(buttons) ? buttons : [buttons];
      var actionFns = [];
      buttons.forEach(function(button) {
        actionFns.push(button.action || noop);
      });
      Efte.send_message('setBarButtons', {
        buttons: buttons
      }, function(result) {
        actionFns[result.index]();
      });
    },

    /* notification publish & subscribe */
    publish: function(name, value) {
      Efte.send_message('publish', {
        name: name,
        value: value
      });
    },

    /* callback(publisher's value); */
    subscribe: function(name, callback) {
      var cid = Efte.send_message('subscribe', {
        name: name
      }, function(result) {
        (typeof callback == 'function') && callback(result.value);
      });
      return {
        unsubscribe: function() {
          delete callbacks[cid];
        }
      }
    },

    multilevelFromResource: function(unit, path, values, callback) {
      Efte.send_message('multilevel', {
        unit: unit,
        path: path,
        values: values
      }, function(result) {
        (typeof callback == 'function') && callback(result.value);
      })
    },

    multilevel: function(options, values, callback) {
      Efte.send_message('multilevel', {
        options: options,
        values: values
      }, function(result) {
        (typeof callback == 'function') && callback(result.value);
      })
    },
    /**
     * datePicker
     * @options  {Object}   options    [description]
     *
     *  type: {String} date|time|datetime(default)
     *  default: {String} 2014-11-11 12:12:12(default) | 2014-11-11 | 12:12:12
     *  ...
     *
     * @callback  {Function} callback [description]
     *
     */

     // deprecated 因为参数过多，现在使用date 这个api
    datePicker: function(type, defaultDate, callback) {
      Efte.send_message('datepicker', {
        'type': type || '',
        'default': defaultDate || ''
      }, function(result) {
        (typeof callback == 'function') && callback(result.value);
      });
    },

    /*
      type: {String} date|time|datetime(default)
      default: {String} 2014-11-11 12:12:12(default) | 2014-11-11 | 12:12:12
      minuteInterval: 分钟间隔，必须能被60整除，例如：1,10,15,30
      minDate: 最小的有效日期，以1970年开始的毫秒数
      maxDate: 最大的有效日期，以1970年开始的毫秒数
    */
    date: function(options, callback) {
      Efte.send_message('datepicker', options, function(result) {
        (typeof callback == 'function') && callback(result.value);
      });
    },

    logout: function() {
      Efte.send_message('logout');
    },

    geo: {
      getCurrentPosition: function(success, error) {
        Efte.send_message('location', {}, function(position) {
          if (!position || !position.status) {
            error(position.message || '定位失败');
          } else {
            success(position);
          }
        });
      }
    },
    showPhoto: function(data, delCallback) {
      var cid = Efte.send_message('showPhoto', data, function(retValue) {
        if (retValue && retValue.action && retValue.action == 'del') {
          delCallback();
        }
        delete callbacks[cid];
      });
    },
    ga: {
      sendEvent: function(category, action, label, value, success, error) {
        var cid = Efte.send_message('ga_sendEvent', {
          category: category,
          action: action,
          label: label,
          value: value
        }, function(data) {
          gaCallbackDelegate(success, error, data, 'ga send event 失败');
          delete callbacks[cid];
        });
      },
      sendView: function(viewName, success, error) {
        var cid = Efte.send_message('ga_sendView', {
          viewName: viewName
        }, function(data) {
          gaCallbackDelegate(success, error, data, 'ga send view 失败');
          delete callbacks[cid];
        });
      },
      sendTiming: function(category, interval, name, label, success, error) {
        var cid = Efte.send_message('ga_sendTiming', {
          category: category,
          interval: interval,
          name: name,
          label: label
        }, function(data) {
          gaCallbackDelegate(success, error, data, 'ga send timing 失败');
          delete callbacks[cid];
        });
      },
      sendException: function(description, fatal, success, error) {
        var cid = Efte.send_message('ga_sendException', {
          description: description,
          fatal: fatal
        }, function(data) {
          gaCallbackDelegate(success, error, data, 'ga send exception 失败');
          delete callbacks[cid];
        });
      },
      setCustomDimension: function(dimentionIndex, value, success, error) {
        var cid = Efte.send_message('ga_setCustomDimension', {
          dimentionIndex: dimentionIndex,
          value: value
        }, function(data) {
          gaCallbackDelegate(success, error, data, 'ga set custom dimension 失败');
          delete callbacks[cid];
        });
      },
      setCustomMetric: function(metricIndex, value, success, error) {
        var cid = Efte.send_message('ga_setCustomMetric', {
          metricIndex: metricIndex,
          value: value
        }, function(data) {
          gaCallbackDelegate(success, error, data, 'ga set custom metric 失败');
          delete callbacks[cid];
        });
      }
    },
    cache: {
      save: function(key, value, opts) {
        opts = opts || {};
        var params = {
          key: key,
          value: value,
          isTemp: !!opts.isTemp
        };

        if (opts.expiration != null) {
          params.expiration = opts.expiration;
        }

        var cid = Efte.send_message('cacheSave', params, function(data) {
          delete callbacks[cid];
          if (!data || !data.status) {
            callbackDelegate(opts.error, [data.message || 'cache保存失败']);
            return;
          }
          callbackDelegate(opts.success, [data.message || 'cache保存成功']);
        });
      },
      load: function(key, opts) {
        opts = opts || {};
        var cid = Efte.send_message('cacheLoad', {
          key: key
        }, function(data) {
          delete callbacks[cid];
          if (!data || !data.status) {
            callbackDelegate(opts.error, [data.message || 'cache读取失败']);
            return;
          }
          callbackDelegate(opts.success, [data]);
        });
      },
      remove: function(key, opts) {
        opts = opts || {};
        var cid = Efte.send_message('cacheDelete', {
          key: key
        }, function(data) {
          delete callbacks[cid];
          if (!data || !data.status) {
            callbackDelegate(opts.error, [data.message || 'cache删除失败']);
            return;
          }
          callbackDelegate(opts.success, [data]);
        });
      },
      removeAll: function(opts) {
        opts = opts || {};
        var cid = Efte.send_message('cacheClear', {}, function(data) {
          delete callbacks[cid];
          if (!data || !data.status) {
            callbackDelegate(opts.error, [data.message || 'cache清除失败']);
            return;
          }
          callbackDelegate(opts.success, [data]);
        });
      }
    },
    push: {
      getInfo: function(success, error) {
        var cid = Efte.send_message('push_getInfo', {}, function(data) {
          delete callbacks[cid];
          if (!data || !data.status) {
            callbackDelegate(error, [data]);
            return;
          }
          callbackDelegate(success, [data]);
        })
      },
      getExtra: function(success, error) {
        var cid = Efte.send_message('push_getExtra', {}, function(data) {
          delete callbacks[cid];
          if (!data || !data.status || !data.extra) {
            callbackDelegate(error, [data]);
            return;
          }
          callbackDelegate(success, [data.extra]);
        });
      }
    }
  };

  try {
    module.exports = Efte;
  } catch (e) {
    window.Efte = Efte;
  }

})();
<|endoftext|>


<commit_before>$(window).on('load', function(){
    setTimeout(function(){window.scrollTo(0,1)},0);
});

//图表
var chart1; // globally available
$(document).ready(function() {
  chart1 = new Highcharts.Chart({
     title: {
        text: ''
     },
     colors: ['#f80', '#fff', '#0ff'],
     chart: {
        renderTo: 'canvas',
        type: 'spline',
        backgroundColor: 'transparent',
        borderColor: '#fff'
     },
     legend: {
        enabled: true,
        verticalAlign: 'top',
        borderWidth: 0,
        style: {
            align: 'right'
        },
        itemStyle: {
            color: '#fff'
        },
        itemHoverStyle: {

        },
        itemHiddenStyle: {

        }
     },
     xAxis: {
        categories: ['15:00', '16:00', '17:00', '18:00', '19:00'],
        lineColor: '#f80',
        tickColor: '#ff8800',
        labels: {
            style: {
                color: '#fff'
            }
        }
     },
     yAxis: {
        title: {
            text: ''
        },
        gridLineColor: '#777',
        max: 100,
        min: 0,
        tickInterval: 50,
        labels: {
            style: {
                color: '#fff'
            }
        }
     },
     series: [{
        name: 'CPU使用率',
        data: [25, 33, 60, 55, 12] // predefined JavaScript array
     }],
     credits: {
        enabled: false
     }
  });

});

//页面管理器====================================================================
var PageManager = function(){
    this.pages = [];
    this.activePage = null;
};
PageManager.prototype = {
    init: function(){
        var _this = this;
        var $pages = $('.page');
        $pages.each(function(i, item){
            //console.log(item)
            var $item = $(item);
            _this.pages.push({
                name: $item.attr('data-page'),
                $: $item
            })
        });
        //console.log(this.pages)
    },
    //页面跳转
    navTo: function(pageName){
        var _this = this;
        var hasPage = false;
        $('.page').removeClass('active');
        $(this.pages).each(function(i, item){
            var $ele = item.$,
                name = item.name;
            if(name === pageName){
                hasPage = true;
                _this.activePage = $ele;
                $ele.addClass('active');
                $(document).trigger('page.' + pageName, pageName);
                return false;
            }
        });
        if(!hasPage){
            console.log('没有该页面');
        }

    },
    //获取当前页
    getActivePage: function(){
        return this.activePage || $('.page.active');
    },
    //获取页面
    getPage: function(pageName){
        var page = null;
        $(this.pages).each(function(i, item){
            var $ele = item.$,
                name = item.name;
            if(name === pageName){
                page = item;
            }
        });
        return page;
    },
    //创建页面
    createPage: function(pageName, pageHtml){
        var name = pageName || 'page_id_' + this.pages.length;
        //var $html = pageHtml || $('
');
        var $html = $('
');
        pageHtml && $html.html(pageHtml);
        $html.attr('data-page', name);
        var page = {
            name: name,
            $: $html
        };
        //添加到DOM
        $('.pages').append($html);

        this.pages.push(page);
        return page;
    },
    //删除页面
    deletePage: function(pageName){
        var _this = this;
        $(this.pages).each(function(i, item){
            var name = item.name;
            if(name === pageName){
                _this.pages.splice(i, 1);
            }
        });
        //从DOM中删除
        $('.page[data-page="' + pageName + '"]').remove();
    },
    //布局控制：占满空间
    stretch: function(){
        this.getActivePage().addClass('stretch');
    },
    //布局控制：占满顶部
    stretchHeader: function(){
        this.getActivePage().addClass('stretch-header');
    },
    //布局控制：占满底部
    stretchFooter: function(){
        this.getActivePage().addClass('stretch-footer');
    },
    //收缩
    shrink: function(){

    }
};

var pageManager = new PageManager();
pageManager.init();

//历史记录管理器============================================================
var HistoryManager = function(){
    this.historys = [];
    //历史信息单位
    var obj = {
        header: {
            html: '',
            fn: function(){}
        },
        footer: {
            html: '',
            fn: function(){}
        },
        page: {
            name: 'list'
        }
    }
};
HistoryManager.prototype = {
    init: function(){

    },
    add: function(obj){
        var header = $.extend(navManager.header, obj.header);
        var footer = $.extend(navManager.footer, obj.footer);
        this.historys.push({
            header: header,
            footer: footer,
            page: obj.page
        });
        console.log('add:', obj.page.name)
    },
    back: function(){
        //alert('back')
        if(this.historys.length < 1){
            return;
        }
        //var obj = this.historys.splice(-1)[0];
        var obj = this.historys.pop();
        navManager.render(obj.header, obj.footer);
        pageManager.navTo(obj.page.name)
        //return obj;
    }
};
var historyManager = new HistoryManager();
historyManager.init();

//导航管理====================================================================
var NavManager = function(){
    var _this = this;
    this.headerWrap = $('.header');
    this.footerWrap = $('.footer');
    /*var defaultHeader = {
        html: template.render('J_tmpl_header'),
        fn: function($html){
            $($html[0]).on('click', function(e){
                e.preventDefault();
                historyManager.back();
            })
        }
    };*/
    var defaultHeader = {
        title: {
            text: "管理控制台",
            class: ""
        },
        left: {
            actions: [
                {
                    type: "back",
                    text: "返回",
                    fn: function(){
                        this.off().on('click', function(e){
                            console.log('1111')
                            e.preventDefault();
                            historyManager.back();
                        })
                    }
                }
            ]
        },
        right: {
            actions: [
                {
                    type: "refresh",
                    text: "刷新",
                    fn: function(){}
                }
            ]
        }
    };
    var defaultFooter = {
        html: template.render('J_tmpl_footer'),
        fn: function($html){
            $html.find('li').on('click', function(e){
                e.preventDefault();
                var $this = $(this);
                $this.addClass('active').siblings().removeClass('active');
                var pageName = $this.attr('data-nav');
                pageManager.navTo(pageName);
            })
        }
    };
    this.header = defaultHeader;
    this.footer = defaultFooter;
};

NavManager.prototype = {
    init: function(){
        this.render();
    },
    render: function(header, footer){
        /*var header = $.extend(this.header, header);
        var footer = $.extend(this.footer, footer);
        var $headerHtml = $(header.html);
        var $footerHtml = $(footer.html);
        this.headerWrap.empty().append($headerHtml).show();
        this.footerWrap.empty().append($footerHtml).show();
        header.fn.call(null, $headerHtml);
        footer.fn.call(null, $footerHtml);*/
        var header = $.extend(this.header, header);
        var footer = $.extend(this.footer, footer);
        this.renderHeader(header);
        this.renderFooter(footer);
    },
    renderHeader: function(header){
        var _this = this;
        var title = header.title;
        var left = header.left;
        var right = header.right;
        var headerLeft = this.headerWrap.find('.header-left');
        var headerRight = this.headerWrap.find('.header-right');
        headerLeft.empty();
        headerRight.empty();
        this.headerWrap.find('.title').html(title.text);
        $(left.actions).each(function(i, item){
            var button = _this.createButton(item);
            headerLeft.append(button);
        });
        $(right.actions).each(function(i, item){
            var button = _this.createButton(item);
            headerRight.append(button);
        });

    },
    renderFooter: function(footer){
        var $footerHtml = $(footer.html);
        this.footerWrap.empty().append($footerHtml).show();
        footer.fn.call(null, $footerHtml);
    },
    createButton: function(obj){
        var button = {
            back: '返回',
            refresh: '刷新'
        }
        var tag = $(button[obj.type]);
        obj.fn.call($(tag));
        return tag;
    }
};
var navManager = new NavManager();
navManager.init();

//应用管理======================================================
var AppManager = function(){

};
AppManager.prototype = {
    init: function(){
        var $document = $(document);
        $document.on('page.login', this.initLoginPage);
        $document.on('page.list', this.initListPage);
        $document.on('page.detail', this.initDetailPage);
        $document.on('page.about', this.initAboutPage);
        //默认使用登录页
        pageManager.navTo('login');

        //动态创建页面例子
        var p = pageManager.createPage();
        p.$.html('
xiaoshan
');
        $document.on('page.' + p.name, function(e, name){
            //alert(name)
        })
        //pageManager.navTo(p.name);
        //pageManager.stretch();
        //end

    },
    initLoginPage: function(){
        navManager.headerWrap.hide();
        navManager.footerWrap.hide();
        pageManager.stretch();
        $('#J_btn_login').off().on('click', function(){
            historyManager.add({
                page: {
                    name: 'login'
                }
            });
            pageManager.navTo('detail');
        });

    },
    initListPage: function(){
        navManager.headerWrap.show();
        var header = {
            title: {
                text: '服务器列表'
            }
        };
        navManager.render(header);

        navManager.footerWrap.show();
        navManager.footerWrap.find('li').removeClass('active').eq(1).addClass('active');
        $('.list-view li').off().on('click', function(){
            historyManager.add({
                header: header,
                page: {
                    name: 'list'
                }
            });
            pageManager.navTo('detail');
        })

    },
    initDetailPage: function(){
        navManager.headerWrap.show();
        navManager.footerWrap.show();
        var header = {
            title: {
                text: '服务器详情'
            },
            right: {
                actions: []
            }
        };
        navManager.render(header);

        navManager.footerWrap.find('li').removeClass('active').eq(3).addClass('active');
        $('.list-group li').off().on('click', function(){
            historyManager.add({
                header: header,
                page: {
                    name: 'detail'
                }
            });
            pageManager.navTo('about');
        })

    },
    initAboutPage: function(){
        var header = {
            title: {
                text: '关于我们'
            }
        };
        navManager.render(header);
        $('#J_create').off().on('click', function(){
            historyManager.add({
                header: header,
                page: {
                    name: 'about'
                }
            });

            var $this= $(this);
            if($this.attr('data-nav')){
                pageManager.navTo($this.attr('data-nav'));
                navManager.render({
                    title: {
                        text: '新页面'
                    }
                });
                return false;
            }
            var p = pageManager.createPage();
            $this.attr('data-nav', p.name);
            p.$.html('
这是个新页面
');
            $(document).on('page.' + p.name, function(e, name){
                //alert(name)
            })
            pageManager.navTo(p.name);
            navManager.render({
                title: {
                    text: '新页面'
                }
            });
            //pageManager.stretch();
        })
    }
};

var appManager = new AppManager();
appManager.init();

<commit_msg>core<commit_after>$(window).on('load', function(){
    setTimeout(function(){window.scrollTo(0,1)},0);
});

//图表
var chart1; // globally available
$(document).ready(function() {
  chart1 = new Highcharts.Chart({
     title: {
        text: ''
     },
     colors: ['#f80', '#fff', '#0ff'],
     chart: {
        renderTo: 'canvas',
        type: 'spline',
        backgroundColor: 'transparent',
        borderColor: '#fff'
     },
     legend: {
        enabled: true,
        verticalAlign: 'top',
        borderWidth: 0,
        style: {
            align: 'right'
        },
        itemStyle: {
            color: '#fff'
        },
        itemHoverStyle: {

        },
        itemHiddenStyle: {

        }
     },
     xAxis: {
        categories: ['15:00', '16:00', '17:00', '18:00', '19:00'],
        lineColor: '#f80',
        tickColor: '#ff8800',
        labels: {
            style: {
                color: '#fff'
            }
        }
     },
     yAxis: {
        title: {
            text: ''
        },
        gridLineColor: '#777',
        max: 100,
        min: 0,
        tickInterval: 50,
        labels: {
            style: {
                color: '#fff'
            }
        }
     },
     series: [{
        name: 'CPU使用率',
        data: [25, 33, 60, 55, 12] // predefined JavaScript array
     }],
     credits: {
        enabled: false
     }
  });

});

//页面管理器====================================================================
var PageManager = function(){
    this.pages = [];
    this.activePage = null;
};
PageManager.prototype = {
    init: function(){
        var _this = this;
        var $pages = $('.page');
        $pages.each(function(i, item){
            //console.log(item)
            var $item = $(item);
            _this.pages.push({
                name: $item.attr('data-page'),
                $: $item
            })
        });
    },
    //页面跳转
    navTo: function(pageName, param){
        var _this = this;
        var hasPage = false;
        var fromPage = this.activePage;
        $('.page').removeClass('active');
        $(this.pages).each(function(i, item){
            var $ele = item.$,
                name = item.name;
            if(name === pageName){
                hasPage = true;
                _this.activePage = item;
                $ele.addClass('active');
                $(document).trigger('page.' + pageName, [pageName, fromPage, param]);//pagename, from, param
                return false;
            }
        });
        if(!hasPage){
            console.log('没有该页面');
        }

    },
    //获取当前页
    getActivePage: function(){
        return this.activePage || (function(){
            var $active = $('.page.active');
            return {
                name: $active.attr('data-page'),
                $: $active
            }
        }());
    },
    //获取页面
    getPage: function(pageName){
        var page = null;
        $(this.pages).each(function(i, item){
            var $ele = item.$,
                name = item.name;
            if(name === pageName){
                page = item;
            }
        });
        return page;
    },
    //创建页面
    createPage: function(pageName, pageHtml){
        var name = pageName || 'page_id_' + this.pages.length;
        //var $html = pageHtml || $('
');
        var $html = $('
');
        pageHtml && $html.html(pageHtml);
        $html.attr('data-page', name);
        var page = {
            name: name,
            $: $html
        };
        //添加到DOM
        $('.pages').append($html);

        this.pages.push(page);
        return page;
    },
    //删除页面
    deletePage: function(pageName){
        var _this = this;
        $(this.pages).each(function(i, item){
            var name = item.name;
            if(name === pageName){
                _this.pages.splice(i, 1);
            }
        });
        //从DOM中删除
        $('.page[data-page="' + pageName + '"]').remove();
    },
    //布局控制：占满空间, 参数page：
    stretch: function(page){
        var pageObj = page || this.getActivePage();
        pageObj.$.addClass('stretch');
    },
    //布局控制：占满顶部
    stretchHeader: function(page){
        var pageObj = page || this.getActivePage();
        pageObj.$.addClass('stretch-header');
    },
    //布局控制：占满底部
    stretchFooter: function(){
        var pageObj = page || this.getActivePage();
        pageObj.$.addClass('stretch-footer');
    },
    //收缩
    shrink: function(){

    }
};

var pageManager = new PageManager();
pageManager.init();

//历史记录管理器============================================================
var HistoryManager = function(){
    this.historys = [];
    //历史信息单位
    var obj = {
        header: {
            html: '',
            fn: function(){}
        },
        footer: {
            html: '',
            fn: function(){}
        },
        page: {
            name: 'list'
        }
    }
};
HistoryManager.prototype = {
    init: function(){

    },
    add: function(obj){
        var header = $.extend(navManager.header, obj.header);
        var footer = $.extend(navManager.footer, obj.footer);
        this.historys.push({
            header: header,
            footer: footer,
            page: obj.page
        });
        console.log('History add:', obj.page.name)
    },
    back: function(){
        if(this.historys.length < 1){
            return;
        }
        var obj = this.historys.splice(-1)[0];
        //var obj = this.historys.pop();
        navManager.render(obj.header, obj.footer);
        pageManager.navTo(obj.page.name)
        console.log('History remove:', obj.page.name)
        //return obj;
    }
};
var historyManager = new HistoryManager();
historyManager.init();

//导航管理====================================================================
var NavManager = function(){
    var _this = this;
    this.headerWrap = $('.header');
    this.footerWrap = $('.footer');
    var defaultHeader = {
        title: {
            text: "管理控制台",
            class: ""
        },
        left: {
            actions: [
                {
                    type: "back",
                    text: "返回",
                    fn: function(){
                        this.off().on('touchstart click', function(e){
                            e.preventDefault();
                            historyManager.back();
                        })
                    }
                }
            ]
        },
        right: {
            actions: [
                {
                    type: "refresh",
                    text: "刷新",
                    fn: function(){}
                }
            ]
        }
    };
    var defaultFooter = {
        html: template.render('J_tmpl_footer'),
        fn: function($html){
            $html.find('li').on('click', function(e){
                e.preventDefault();
                var $this = $(this);
                $this.addClass('active').siblings().removeClass('active');
                var pageName = $this.attr('data-nav');
                pageManager.navTo(pageName);
            })
        }
    };
    this.header = defaultHeader;
    this.footer = defaultFooter;
};

NavManager.prototype = {
    init: function(){
        this.render();
    },
    render: function(header, footer){
        var header = $.extend(this.header, header);
        var footer = $.extend(this.footer, footer);
        this.renderHeader(header);
        this.renderFooter(footer);
    },
    renderHeader: function(header){
        var _this = this;
        var title = header.title;
        var left = header.left;
        var right = header.right;
        var headerLeft = this.headerWrap.find('.header-left');
        var headerRight = this.headerWrap.find('.header-right');
        headerLeft.empty();
        headerRight.empty();
        this.headerWrap.find('.title').html(title.text);
        $(left.actions).each(function(i, item){
            var button = _this.createButton(item);
            headerLeft.append(button);
        });
        $(right.actions).each(function(i, item){
            var button = _this.createButton(item);
            headerRight.append(button);
        });

    },
    renderFooter: function(footer){
        var $footerHtml = $(footer.html);
        this.footerWrap.empty().append($footerHtml).show();
        footer.fn.call(null, $footerHtml);
    },
    createButton: function(obj){
        var button = {
            back: '返回',
            refresh: '刷新'
        }
        var tag = $(button[obj.type]);
        tag.html(obj.text);
        obj.fn.call($(tag));
        return tag;
    }
};
var navManager = new NavManager();
navManager.init();

//应用管理======================================================
var AppManager = function(){

};
AppManager.prototype = {
    init: function(){
        var $document = $(document);
        $document.on('page.login', this.initLoginPage);
        $document.on('page.list', this.initListPage);
        $document.on('page.detail', this.initDetailPage);
        $document.on('page.about', this.initAboutPage);
        //默认使用登录页
        pageManager.navTo('login');

        //动态创建页面例子
        var p = pageManager.createPage();
        p.$.html('
xiaoshan
');
        $document.on('page.' + p.name, function(e, name){
            //alert(name)
        })
        //pageManager.navTo(p.name);
        //pageManager.stretch();
        //end
    },
    initLoginPage: function(e, pageName, fromPage, param){
        //console.log(fromPage)
        navManager.headerWrap.hide();
        navManager.footerWrap.hide();
        pageManager.stretch();
        $('#J_btn_login').off().on('click', function(){
            historyManager.add({
                page: {
                    name: 'login'
                }
            });
            pageManager.navTo('detail');
        });

    },
    initListPage: function(e, pageName, fromPage, param){
        //console.log(fromPage)
        navManager.headerWrap.show();
        var header = {
            title: {
                text: '服务器列表'
            }
        };
        navManager.render(header);

        navManager.footerWrap.show();
        navManager.footerWrap.find('li').removeClass('active').eq(1).addClass('active');
        $('.list-view li').off().on('click', function(){
            historyManager.add({
                header: header,
                page: {
                    name: 'list'
                }
            });
            pageManager.navTo('detail');
        })

    },
    initDetailPage: function(e, pageName, fromPage, param){
        //console.log(fromPage)
        navManager.headerWrap.show();
        navManager.footerWrap.show();
        var header = {
            title: {
                text: '服务器详情'
            },
            right: {
                actions: []
            }
        };
        navManager.render(header);

        navManager.footerWrap.find('li').removeClass('active').eq(3).addClass('active');
        $('.list-group li').off().on('click', function(){
            historyManager.add({
                header: header,
                page: {
                    name: 'detail'
                }
            });
            pageManager.navTo('about');
        })

    },
    initAboutPage: function(e, pageName, fromPage, param){
        var header = {
            title: {
                text: '关于我们'
            }
        };
        navManager.render(header);
        $('#J_create').off().on('click', function(){
            historyManager.add({
                header: header,
                page: {
                    name: 'about'
                }
            });

            var $this= $(this);
            if($this.attr('data-nav')){
                pageManager.navTo($this.attr('data-nav'));
                navManager.render({
                    title: {
                        text: '新页面'
                    }
                });
                return false;
            }
            var p = pageManager.createPage();
            $this.attr('data-nav', p.name);
            p.$.html('
这是个新页面
');
            $(document).on('page.' + p.name, function(e, name){
                //alert(name)
            })
            pageManager.navTo(p.name);
            navManager.render({
                title: {
                    text: '新页面'
                }
            });
            //pageManager.stretch();
        })
    }
};

var appManager = new AppManager();
appManager.init();

<|endoftext|>


<commit_before>var fs = require("fs"),
    async = require("async"),
    request = require("request");

exports.linkJar = function (jar, callback) {
    var dest = this.jar;

    async.waterfall([
        function (done) {
            fs.exists(dest, function (exists) {
                done(null, exists);
            });
        },
        function (exists, done) {
            if (exists) {
                fs.unlink(dest, done);
            } else {
                process.nextTick(done);
            }
        },
        function (done) {
            fs.symlink(jar, dest, done);
        }
    ], callback.bind(this));
};

exports.downloadJar = function (url, callback) {
    var src = request(url);
    callback = callback.bind(this);

    src.pipe(fs.createWriteStream(this.jar));
    src.on("end", callback);
    src.on("error", callback);
};
<commit_msg>adding Server#runnable() to determine if a server has a jar to start the server with<commit_after>var fs = require("fs"),
    async = require("async"),
    request = require("request");

exports.hasJar = function (callback) {
    return fs.exists(this.jar, callback.bind(this));
};

exports.linkJar = function (jar, callback) {
    var dest = this.jar;

    async.waterfall([
        function (done) {
            fs.exists(dest, function (exists) {
                done(null, exists);
            });
        },
        function (exists, done) {
            if (exists) {
                fs.unlink(dest, done);
            } else {
                process.nextTick(done);
            }
        },
        function (done) {
            fs.symlink(jar, dest, done);
        }
    ], callback.bind(this));
};

exports.downloadJar = function (url, callback) {
    var src = request(url);
    callback = callback.bind(this);

    src.pipe(fs.createWriteStream(this.jar));
    src.on("end", callback);
    src.on("error", callback);
};
<|endoftext|>


<commit_before>$(document).ready(function(){
  // Change this to the correct selector.
  //$('#fixed-header').midnight();
  $("#part-0").css("min-height",$(window).height() - 0);

  $( window ).resize(function() {
    $("#part-0").css("min-height",$(window).height() - 0);
  });
});

$(".main-header-1").fitText();
$("#part-0 > div > div > h3").fitText(0.8);
$("#part-0 > div > div > h4").fitText(0.6);
$("#part-0 > div > div > h5").fitText(1.5);

var side_open = false;
var is_mobile = false;

if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
 	is_mobile = true;
}

if(is_mobile){

}

var editor = CodeMirror(document.getElementById("code"),{
    lineNumbers: true,
    mode: "swift",
    theme: "pastel-on-dark",
    lineWrapping: false,
    indentUnit: 4,
    indentWithTabs: true
});
var txtFile = new XMLHttpRequest();
txtFile.open("GET", "lib/intro.swift", true);
txtFile.onreadystatechange = function()
{
	if (txtFile.readyState === 4) {  // document is ready to parse.
		if (txtFile.status === 200) {  // file is found
			var allText = txtFile.responseText;
			editor.setValue(allText);
		}
	}
}
txtFile.send(null);

function skill(){
  $('#part-2').slideToggle();
  if($("#skill").attr("shape") === "cancel"){
    $("#skill").attr("shape","plus");
  }
  else{
    $("#skill").attr("shape","cancel");
  }
}

function cyc(){
  $('#part-3').slideToggle();
  if($("#cyc").attr("shape") === "cancel"){
    $("#cyc").attr("shape","plus");
  }
  else{
    $("#cyc").attr("shape","cancel");
  }
}

function proj(){
  $('#part-4').slideToggle({
    complete: function(){
      editor.refresh();
    }
  });

  if($("#proj").attr("shape") === "cancel"){
    $("#proj").attr("shape","plus");
  }
  else{
    $("#proj").attr("shape","cancel");
  }
}

function xp(){
  $('#part-6').slideToggle();
  if($("#xp").attr("shape") === "cancel"){
    $("#xp").attr("shape","plus");
  }
  else{
    $("#xp").attr("shape","cancel");
  }
}
<commit_msg>Update main.js<commit_after>$(document).ready(function(){
  // Change this to the correct selector.
  //$('#fixed-header').midnight();
  $("#part-0").css("min-height",$(window).height() - 0);

  $( window ).resize(function() {
    $("#part-0").css("min-height",$(window).height() - 0);
  });
});

$(".main-header-1").fitText();
$("#part-0 > div > div > h3").fitText(0.8);
$("#part-0 > div > div > h4").fitText(0.6);
$("#part-0 > div > div > h5").fitText(1.5);
$("#name").fitText();

var side_open = false;
var is_mobile = false;

if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
 	is_mobile = true;
}

if(is_mobile){

}

var editor = CodeMirror(document.getElementById("code"),{
    lineNumbers: true,
    mode: "swift",
    theme: "pastel-on-dark",
    lineWrapping: false,
    indentUnit: 4,
    indentWithTabs: true
});
var txtFile = new XMLHttpRequest();
txtFile.open("GET", "lib/intro.swift", true);
txtFile.onreadystatechange = function()
{
	if (txtFile.readyState === 4) {  // document is ready to parse.
		if (txtFile.status === 200) {  // file is found
			var allText = txtFile.responseText;
			editor.setValue(allText);
		}
	}
}
txtFile.send(null);

function skill(){
  $('#part-2').slideToggle();
  if($("#skill").attr("shape") === "cancel"){
    $("#skill").attr("shape","plus");
  }
  else{
    $("#skill").attr("shape","cancel");
  }
}

function cyc(){
  $('#part-3').slideToggle();
  if($("#cyc").attr("shape") === "cancel"){
    $("#cyc").attr("shape","plus");
  }
  else{
    $("#cyc").attr("shape","cancel");
  }
}

function proj(){
  $('#part-4').slideToggle({
    complete: function(){
      editor.refresh();
    }
  });

  if($("#proj").attr("shape") === "cancel"){
    $("#proj").attr("shape","plus");
  }
  else{
    $("#proj").attr("shape","cancel");
  }
}

function xp(){
  $('#part-6').slideToggle();
  if($("#xp").attr("shape") === "cancel"){
    $("#xp").attr("shape","plus");
  }
  else{
    $("#xp").attr("shape","cancel");
  }
}
<|endoftext|>


<commit_before>'use strict';
const EventEmitter = require('events');
const assert = require('assert');
const courier = require('./courier');
/**
 * 工作单位，用来表示工作的内容、相关时间与状态
 * - id: 工作号
 * - data: 工作数据
 * - createdAt: 创建时间，默认为创建时时间，类型为时间戳数据
 * - failedAt: 失败时间，默认为0，类型为时间戳数据
 * - updatedAt: 更新时间，默认为创建时间，类型为时间戳数据
 * - promotedAt: 开始处理时间，默认为0，类型为时间戳数据（考虑改成进入就绪工作时间，而开始处理时间由 startedAt 表示）
 * - progress: 进度数据，默认为0，类型为从 0 - 100 的整数
 * - error: 描述错误发生的字符串，默认为空（考虑去掉）
 * - attempts: 1 失败尝试次数
 * - maxAttempts: 1 最多允许失败尝试次数
 * - delay: 默认为0，标示延迟多少毫秒执行
 * - priority: -10 任务优先级，未实现
 * - ttl: 默认是60 * 60（1 小时），单位是秒
 * - type: 任务类型
 * - state: 任务状态，字符串表达
 */
class Job extends EventEmitter {
  constructor(type, data) {
    super();
    this.data = JSON.stringify(data);
    this.createdAt = Date.now();
    this.failedAt = 0;
    this.updatedAt = this.createdAt;
    this.promotedAt = 0;
    this.progress = 0;
    this.error = '';
    this.attempts = 1;
    this.maxAttempts = 1;
    this.delay = 0;
    this.priority = -10;
    this.type = type;
    this.ttl = 60 * 60;
    this.state = 'ready'; // 默认是就绪状态

  }

  /**
   * 初始化
   * @param id
   * @param pubsub
   * @param redis
   */
  init(id, pubsub, redis) {
    this.id = id;
    this.redis = redis;
    const channel = `events:${id}`;
    pubsub.subscribe(channel);
    pubsub.on('message', (revChannel, message) => {
      if (revChannel === channel) {
        const msg = JSON.parse(message);
        this.emit(msg.name, this, msg.data);
      }
    });
  }

  toJSON() {
    return {
      id: this.id,
      data: this.data,
      createdAt: this.createdAt,
      failedAt: this.failedAt,
      updatedAt: this.updatedAt,
      promotedAt: this.promotedAt,
      progress: this.progress,
      error: this.error,
      attempts: this.attempts,
      maxAttempts: this.maxAttempts,
      delay: this.delay,
      priority: this.priority,
      ttl: this.ttl,
      type: this.type,
      state: this.state,
    };
  }

  /**
   *
   * 切换状态，在系统实现中就是重新排队
   * 系统中每一个状态都是一个队列，从一个状态切换到另一个状态的时候，就需要重新排队
   *
   * @param from - 原来的状态，可以为空，但是必须明确是 null
   * @param to - 到什么队列，不可以为空
   * @param callback
   */
  requeue(from, to, callback) {
    assert(to, 'to 不可以为空！');
    const client = this.redis.client;
    const id = this.id;
    assert(id != null);
    assert(client !=  null);

    client.multi()
      .hmset(`job:${id}`, this.toJSON())
      .lpush(`jobs:${to}`, id)
      .lpush(`jobs:${this.type}:${to}`, id)
      .hset(`job:${id}`, 'state', to)
      .exec(err => {
        if (err) return callback(err);

        client.publish(`events:${id}`, JSON.stringify({
          id: id,
          name: `${from} -> ${to}`,
        }));
        callback();
      });
  }

  log() {

  }
}

module.exports = Job;
<commit_msg>增加日志记录功能<commit_after>'use strict';
const EventEmitter = require('events');
const assert = require('assert');
const courier = require('./courier');
/**
 * 工作单位，用来表示工作的内容、相关时间与状态
 * - id: 工作号
 * - data: 工作数据
 * - createdAt: 创建时间，默认为创建时时间，类型为时间戳数据
 * - failedAt: 失败时间，默认为0，类型为时间戳数据
 * - updatedAt: 更新时间，默认为创建时间，类型为时间戳数据
 * - promotedAt: 开始处理时间，默认为0，类型为时间戳数据（考虑改成进入就绪工作时间，而开始处理时间由 startedAt 表示）
 * - progress: 进度数据，默认为0，类型为从 0 - 100 的整数
 * - error: 描述错误发生的字符串，默认为空（考虑去掉）
 * - attempts: 1 失败尝试次数
 * - maxAttempts: 1 最多允许失败尝试次数
 * - delay: 默认为0，标示延迟多少毫秒执行
 * - priority: -10 任务优先级，未实现
 * - ttl: 默认是60 * 60（1 小时），单位是秒
 * - type: 任务类型
 * - state: 任务状态，字符串表达
 */
class Job extends EventEmitter {
  constructor(type, data) {
    super();
    this.data = JSON.stringify(data);
    this.createdAt = Date.now();
    this.failedAt = 0;
    this.updatedAt = this.createdAt;
    this.promotedAt = 0;
    this.progress = 0;
    this.error = '';
    this.attempts = 1;
    this.maxAttempts = 1;
    this.delay = 0;
    this.priority = -10;
    this.type = type;
    this.ttl = 60 * 60;
    this.state = 'ready'; // 默认是就绪状态

  }

  /**
   * 初始化
   * @param id
   * @param pubsub
   * @param redis
   */
  init(id, pubsub, redis) {
    this.id = id;
    this.redis = redis;
    const channel = `events:${id}`;
    pubsub.subscribe(channel);
    pubsub.on('message', (revChannel, message) => {
      if (revChannel === channel) {
        const msg = JSON.parse(message);
        this.emit(msg.name, this, msg.data);
      }
    });
  }

  toJSON() {
    return {
      id: this.id,
      data: this.data,
      createdAt: this.createdAt,
      failedAt: this.failedAt,
      updatedAt: this.updatedAt,
      promotedAt: this.promotedAt,
      progress: this.progress,
      error: this.error,
      attempts: this.attempts,
      maxAttempts: this.maxAttempts,
      delay: this.delay,
      priority: this.priority,
      ttl: this.ttl,
      type: this.type,
      state: this.state,
    };
  }

  /**
   *
   * 切换状态，在系统实现中就是重新排队
   * 系统中每一个状态都是一个队列，从一个状态切换到另一个状态的时候，就需要重新排队
   *
   * @param from - 原来的状态，可以为空，但是必须明确是 null
   * @param to - 到什么队列，不可以为空
   * @param callback
   */
  requeue(from, to, callback) {
    assert(to, 'to 不可以为空！');
    const client = this.redis.client;
    const id = this.id;
    assert(id != null);
    assert(client !=  null);

    client.multi()
      .hmset(`job:${id}`, this.toJSON())
      .lpush(`jobs:${to}`, id)
      .lpush(`jobs:${this.type}:${to}`, id)
      .hset(`job:${id}`, 'state', to)
      .exec(err => {
        if (err) return callback(err);

        client.publish(`events:${id}`, JSON.stringify({
          id: id,
          name: `${from} -> ${to}`,
        }));
        callback();
      });
  }

  /**
   * 记录工作日志
   * @param data 只允许传入字符串，如果其他类型自行转换称字符串
   */
  log(data) {
    const client = this.redis.client;
    const id = this.id;
    client.lpush(`job:${id}`, data);
  }
}

module.exports = Job;
<|endoftext|>


<commit_before>$(document).ready(function(){
    var createTaskButton = $('#createTask');

    createTaskButton.on('click', function(e){
        taskInput = $('.todo-input-text');
        taskString = taskInput.val();

        if(taskString != ''){
            var taskList = $('#task-list');

            var taskEntry = $('

');
            var checkedColumn = $('<input type="checkbox" class="col-xs-1">');
            var titleColumn = $('
' + taskString + '
');

            checkedColumn.change(function(e){
                titleColumn.toggleClass('deleted-text');
            });

            var deleteColumn = $('');

            deleteColumn.on('click', function(e){
                deleteColumn.parent().remove();
            });

            taskEntry.append(checkedColumn);
            taskEntry.append(titleColumn);
            taskEntry.append(deleteColumn);

            taskList.append(taskEntry);

            clearInput(taskInput);
        }

        else{
            $("#alertEmptyField").removeClass('hide');
            setTimeout(function() { $("#alertEmptyField").addClass('hide'); }, 1500);
        }
    });

});

var clearInput = function(input){
    input.val('');
};

var deleteTask = function(taskId){
    // it would be great to implement deletion this way;
};
<commit_msg>Smal code-style improvements.<commit_after>$(document).ready(function(){
    var createTaskButton = $('#createTask');

    createTaskButton.on('click', function(e){
        taskInput = $('.todo-input-text');
        taskString = taskInput.val();

        if(taskString !== ''){
            var taskList = $('#task-list');

            var taskEntry = $('

');
            var checkedColumn = $('<input type="checkbox" class="col-xs-1">');
            var titleColumn = $('
' + taskString + '
');

            checkedColumn.change(function(e){
                titleColumn.toggleClass('deleted-text');
            });

            var deleteColumn = $('');

            deleteColumn.on('click', function(e){
                deleteColumn.parent().remove();
            });

            taskEntry.append(checkedColumn);
            taskEntry.append(titleColumn);
            taskEntry.append(deleteColumn);

            taskList.append(taskEntry);

            clearInput(taskInput);
        } else {
            $("#alertEmptyField").removeClass('hide');
            setTimeout(function() {
                $("#alertEmptyField").addClass('hide');
            }, 1500);
        }
    });

});

var clearInput = function(input){
    input.val('');
};

var deleteTask = function(taskId){
    // it would be great to implement deletion this way;
};
<|endoftext|>