<commit_before>function RallyUI(rally) {
    this.rally = rally;
    var ui = this;

    $('#add-instruction').on('click', function (e) {
        ui.rally.addNextInstruction().then(function (row) {
            ui.selectInstruction(row);
        });
    });

    $('#edit-instruction').on('click', function (e) {
        ui.editMode(!ui.editState);
    });

    $('#instructions').on('keydown', function (e) {
        console.log('TODO: For keys relating to cells, determine current cell and invoke cell keydown; others handle directly');
    });

    var rally_speed = $('#edit-rally-speed');
    rally_speed.on('blur', function (e) {
        rally.rallySpeed($(this).val());
        ui.renderInstructions();
    });
    rally_speed.val(rally.rallySpeed());

}

RallyUI.prototype = {
    checkVal: function(val) {
        if (typeof val == 'undefined' || val == null) {
            return 'ERR';
        }
        return val;
    },

    editState: false,

    selectedInstruction: null,

    columnState: [],

    showColumn(index, show) {
        if (show) {
            $('th[data-col=\''+index+'\']').show();
            $('td[data-col=\''+index+'\']').show();
        } else {
            $('th[data-col=\''+index+'\']').hide();
            $('td[data-col=\''+index+'\']').hide();
        }
        $('label[data-col=\''+index+'\'] input').prop('checked', show);
        this.columnState[index] = show;

        // Force table layout to update
        $('#instructions').trigger( "resize" );
    },

    renderInstructionsHeader: function() {
        var table = $('#instructions');
        var thead = table.children('thead');
        var hideshow = $('#hideshow');
        var ui = this;

        thead.children().remove();
        hideshow.children().remove();

        var tr = $('<tr />');
        RallyInstruction.prototype.columnDefs.forEach(function (col) {
            var th = $('<th />').attr('data-col', col.index);
            th.html(col.label);

            th.on('dblclick', function (e) {
                ui.showColumn(col.index, false);
            });
            tr.append(th);

            var input = $('<input type=checkbox checked/>');
            input.on('change', function (e) {
                ui.showColumn(col.index, e.target.checked);
            });

            hideshow.append($('<label />').attr('data-col', col.index).append(input).append(col.label));

            ui.columnState[col.index] = true;
        });

        thead.append(tr);

        $.contextMenu({
            selector: 'thead th',
            items: {
                hide: {
                    name: 'Hide',
                    callback: function (key, opt) {
                        var index = this.attr('data-col');
                        ui.showColumn(index, false);
                        return true;
                    },
                },
            },
        });

        var instrFromRow = function(element) {
            return Number.parseFloat(element.attr('data-row'));
        }
        $.contextMenu({
            selector: 'tr td',
            items: {
                insert_before: {
                    name: 'Insert before',
                    callback: function (key, opt) {
                        var tr = this.closest('tr');
                        var instr = instrFromRow(tr);
                        ui.rally.addInstruction(instr - 0.5);
                        return true;
                    },
                },
                insert_after: {
                    name: 'Insert after',
                    callback: function (key, opt) {
                        var tr = this.closest('tr');
                        var instr = instrFromRow(tr);
                        ui.rally.addInstruction(instr + 0.5);
                        return true;
                    },
                },

                separator1: { "type": "cm_seperator" },

                delete: {
                    name: 'Delete',
                    callback: function (key, opt) {
                        var tr = this.closest('tr');
                        var instr = instrFromRow(tr);
                        ui.rally.deleteInstruction(instr);
                        return true;
                    },
                },
            },
        });

        // Must be after contextMenu to prevent buggy item callbacks
        table.floatThead();
    },

    renderInstructions: function() {
        var tbody = $('#instructions').children('tbody');
        tbody.children().remove();
        var ui = this;
        var instructions = ui.rally.instructions;
        this.rally.sortedKeys().forEach(function (i) {
            ui.renderInstruction(instructions[i]);
        });
        $('#instructions').trigger( "resize" );
    },

    renderInstruction: function(instr) {
        var tbody = $('#instructions').children('tbody');
        var ui = this;
        var tr = $('<tr />').attr('data-row', instr.instr);

        instr.columns.forEach(function (col, index) {
            var td = $('<td />').attr('data-col', index);
            var calculated = col.isCalculated();
            if (!calculated) {
                td.addClass('notcalc');
            }
            if (ui.editState && ui.selectedInstruction == instr.instr && col.is_db) {
                var input = $('<input />');
                input.attr('type', 'text');
                input.attr('size', 5);
                if (calculated) {
                    input.attr('placeholder', col.display_value);
                } else {
                    input.val(col.calculated_value);
                }
                input.on('keydown', function (e) {
                    ui.handleKeyDown(e, instr);
                });
                input.on('blur', function (e) {
                    var val = null;
                    if (input.val().length > 0) {
                        val = input.val();
                    }
                    if (val != col.value) {
                        ui.rally.setValue(instr.instr, col.index, val);
                    }
                });
                td.append(input);
                td.addClass('edit');
            } else {
                var val = col.toString();
                if (col.name == 'delay') {
                    if (val == 0) {
                        val = '';
                    }
                } else if (typeof val == 'number') {
                    if (col.name == 'instr') {
                        val = val.toFixed(1);
                    } else if (col.name == 'd_time') {
                        val = moment('0:0', 'm:s').add(val, 'seconds').format('m:ss');
                    } else if (col.name != 'cas') {
                        val = val.toFixed(3);
                    }

                }
                td.html(val);
            }
            if (col.name == 'd_mlg') {
                if (val <= 0.15 && val > 0) {
                    td.addClass('danger');
                }
            } else if (col.name == 'cas') {
                if (val > 0 && val != rally.rallySpeed()) {
                    td.addClass('warning');
                }
            }
            tr.append(td);

            if (!ui.columnState[index])
                td.hide();
        });
        if (ui.selectedInstruction == instr.instr) {
            tr.addClass('active');
        }

        tr.on('click', function (e) {
            if (ui.selectedInstruction != instr.instr) {
                ui.selectInstruction(instr.instr, {row: instr.instr, col: $(e.target).attr('data-col')});
            }
        });

        tr.on('dblclick', function (e) {
            if (!ui.editState) {
                ui.editMode(true, {row: instr.instr, col: $(e.target).attr('data-col')});
            }
        });

        var old_tr = tbody.children('tr[data-row=\''+instr.instr+'\']');
        if (old_tr.length > 0) {
            tr.insertBefore(old_tr);
            old_tr.remove();
        } else {
            tbody.append(tr);
        }
    },

    handleKeyDown(e, instr) {
        var ui = this;
        var input = $(e.target);
        var index = Number.parseFloat(input.closest('td').attr('data-col'));
        var handled = true;
        switch(e.keyCode) {
            case 33: // page up
                var i = instr;
                var count = 5;
                for (var count = 5; count > 0; count--) {
                    if (i.prev) {
                        i = i.prev;
                    }
                }
                if (i != instr) {
                    ui.selectInstruction(i.instr, {row: i.instr, col: index});
                }
                break;
            case 34: // page down
                var i = instr;
                var count = 5;
                for (var count = 5; count > 0; count--) {
                    if (i.next) {
                        i = i.next;
                    }
                }
                if (i != instr) {
                    ui.selectInstruction(i.instr, {row: i.instr, col: index});
                }
                break;
            case 38: // up
                if (instr.prev) {
                    ui.selectInstruction(instr.prev.instr, {row: instr.prev.instr, col: index});
                }
                break;
            case 40: // down
                var next = instr.next;
                if (!next) {
                    ui.rally.addNextInstruction().then(function (row) {
                        ui.selectInstruction(row, {row: row, col: index});
                    });
                }
                if (instr.next) {
                    ui.selectInstruction(instr.next.instr, {row: instr.next.instr, col: index});
                }
                break;
            case 27: // escape
                ui.editMode(false);
                break;
            case 46: // delete
                if (e.shiftKey) {
                    if (instr.next) {
                        ui.selectInstruction(instr.next.instr, {row: instr.next.instr, col: index});
                    } else if (instr.prev) {
                        ui.selectInstruction(instr.prev.instr, {row: instr.prev.instr, col: index});
                    }
                    ui.editMode(false);
                    ui.rally.deleteInstruction(instr.instr);
                }
                break;
            default:
                handled = false;
                break;
        }
        if (handled) {
            e.stopPropagation();
            e.preventDefault();
        }
    },

    findRow: function(instr) {
        return $('tr[data-row=\''+instr+'\']');
    },

    findCell: function(instr, column) {
        return $('tr[data-row=\''+instr+'\']').children('td[data-col=\''+column+'\']');
    },

    selectInstruction: function (instr, target) {
        $('tr.active').removeClass('active');
        var old_instr = this.selectedInstruction;
        instr = Number.parseFloat(instr);
        if (isNaN(instr)) {
            instr = null;
        }
        if (target) {
            target.row = Number.parseFloat(target.row);
            target.col = Number.parseInt(target.col);
        }
        if (old_instr != null) {
            var old_tr = $('tr[data-row=\''+old_instr+'\']');
            old_tr.find('input').trigger('blur');
        }
        if (instr != null) {
            this.selectedInstruction = instr;
            this.renderInstruction(this.rally.instructions[instr]);
            if (old_instr != null) {
                this.renderInstruction(this.rally.instructions[old_instr]);
            }
            var tr = $('tr[data-row=\''+instr+'\']');
            if (this.editState && target) {
                var td = this.findCell(target.row, target.col);
                var input = td.find('input');
                input.focus();
                input.select();
            }
            tr.addClass('active');
        }
    },

    editMode: function (state, target) {
        if (typeof state != 'undefined') {
            this.editState = state;
        }
        if (this.selectedInstruction != null) {
            this.renderInstruction(this.rally.instructions[this.selectedInstruction]);
            if (this.editState && target) {
                var tr = this.findRow(target.row);
                var td = this.findCell(target.row, target.col);
                var input = td.find('input');
                input.focus();
                input.select();
            }
        }
        var editButton = $('#edit-instruction');
        if (state) {
            editButton.addClass('active');
        } else {
            editButton.removeClass('active');
        }
    },
};
<commit_msg>RallyUI: Refactor selected row/col<commit_after>function RallyUI(rally) {
    this.rally = rally;
    var ui = this;

    $('#add-instruction').on('click', function (e) {
        ui.rally.addNextInstruction().then(function (row) {
            ui.selectInstruction(row);
        });
    });

    $('#edit-instruction').on('click', function (e) {
        ui.editMode(!ui.editState);
    });

    $('#instructions').on('keydown', function (e) {
        console.log('TODO: For keys relating to cells, determine current cell and invoke cell keydown; others handle directly');
    });

    var rally_speed = $('#edit-rally-speed');
    rally_speed.on('blur', function (e) {
        rally.rallySpeed($(this).val());
        ui.renderInstructions();
    });
    rally_speed.val(rally.rallySpeed());

}

RallyUI.prototype = {
    checkVal: function(val) {
        if (typeof val == 'undefined' || val == null) {
            return 'ERR';
        }
        return val;
    },

    editState: false,

    selected: null,

    columnState: [],

    showColumn(index, show) {
        if (show) {
            $('th[data-col=\''+index+'\']').show();
            $('td[data-col=\''+index+'\']').show();
        } else {
            $('th[data-col=\''+index+'\']').hide();
            $('td[data-col=\''+index+'\']').hide();
        }
        $('label[data-col=\''+index+'\'] input').prop('checked', show);
        this.columnState[index] = show;

        // Force table layout to update
        $('#instructions').trigger( "resize" );
    },

    renderInstructionsHeader: function() {
        var table = $('#instructions');
        var thead = table.children('thead');
        var hideshow = $('#hideshow');
        var ui = this;

        thead.children().remove();
        hideshow.children().remove();

        var tr = $('<tr />');
        RallyInstruction.prototype.columnDefs.forEach(function (col) {
            var th = $('<th />').attr('data-col', col.index);
            th.html(col.label);

            th.on('dblclick', function (e) {
                ui.showColumn(col.index, false);
            });
            tr.append(th);

            var input = $('<input type=checkbox checked/>');
            input.on('change', function (e) {
                ui.showColumn(col.index, e.target.checked);
            });

            hideshow.append($('<label />').attr('data-col', col.index).append(input).append(col.label));

            ui.columnState[col.index] = true;
        });

        thead.append(tr);

        $.contextMenu({
            selector: 'thead th',
            items: {
                hide: {
                    name: 'Hide',
                    callback: function (key, opt) {
                        var index = this.attr('data-col');
                        ui.showColumn(index, false);
                        return true;
                    },
                },
            },
        });

        var instrFromRow = function(element) {
            return Number.parseFloat(element.attr('data-row'));
        }
        $.contextMenu({
            selector: 'tr td',
            items: {
                insert_before: {
                    name: 'Insert before',
                    callback: function (key, opt) {
                        var tr = this.closest('tr');
                        var instr = instrFromRow(tr);
                        ui.rally.addInstruction(instr - 0.5);
                        return true;
                    },
                },
                insert_after: {
                    name: 'Insert after',
                    callback: function (key, opt) {
                        var tr = this.closest('tr');
                        var instr = instrFromRow(tr);
                        ui.rally.addInstruction(instr + 0.5);
                        return true;
                    },
                },

                separator1: { "type": "cm_seperator" },

                delete: {
                    name: 'Delete',
                    callback: function (key, opt) {
                        var tr = this.closest('tr');
                        var instr = instrFromRow(tr);
                        ui.rally.deleteInstruction(instr);
                        return true;
                    },
                },
            },
        });

        // Must be after contextMenu to prevent buggy item callbacks
        table.floatThead();
    },

    renderInstructions: function() {
        var tbody = $('#instructions').children('tbody');
        tbody.children().remove();
        var ui = this;
        var instructions = ui.rally.instructions;
        this.rally.sortedKeys().forEach(function (i) {
            ui.renderInstruction(instructions[i]);
        });
        $('#instructions').trigger( "resize" );
    },

    renderInstruction: function(instr) {
        var tbody = $('#instructions').children('tbody');
        var ui = this;
        var tr = $('<tr />').attr('data-row', instr.instr);

        instr.columns.forEach(function (col, index) {
            var td = $('<td />').attr('data-col', index);
            var calculated = col.isCalculated();
            if (!calculated) {
                td.addClass('notcalc');
            }
            if (ui.editState && ui.isSelected(instr.instr) && col.is_db) {
                var input = $('<input />');
                input.attr('type', 'text');
                input.attr('size', 5);
                if (calculated) {
                    input.attr('placeholder', col.display_value);
                } else {
                    input.val(col.calculated_value);
                }
                input.on('keydown', function (e) {
                    ui.handleKeyDown(e, instr);
                });
                input.on('blur', function (e) {
                    var val = null;
                    if (input.val().length > 0) {
                        val = input.val();
                    }
                    if (val != col.value) {
                        ui.rally.setValue(instr.instr, col.index, val);
                    }
                });
                td.append(input);
                td.addClass('edit');
            } else {
                var val = col.toString();
                if (col.name == 'delay') {
                    if (val == 0) {
                        val = '';
                    }
                } else if (typeof val == 'number') {
                    if (col.name == 'instr') {
                        val = val.toFixed(1);
                    } else if (col.name == 'd_time') {
                        val = moment('0:0', 'm:s').add(val, 'seconds').format('m:ss');
                    } else if (col.name != 'cas') {
                        val = val.toFixed(3);
                    }

                }
                td.html(val);
            }
            if (col.name == 'd_mlg') {
                if (val <= 0.15 && val > 0) {
                    td.addClass('danger');
                }
            } else if (col.name == 'cas') {
                if (val > 0 && val != rally.rallySpeed()) {
                    td.addClass('warning');
                }
            }
            tr.append(td);

            if (!ui.columnState[index])
                td.hide();
        });

        tr.on('click', function (e) {
            if (!ui.isSelected(instr.instr)) {
                ui.selectInstruction(instr.instr, {row: instr.instr, col: $(e.target).attr('data-col')});
            }
        });

        tr.on('dblclick', function (e) {
            if (!ui.editState) {
                ui.editMode(true, {row: instr.instr, col: $(e.target).attr('data-col')});
            }
        });

        var old_tr = tbody.children('tr[data-row=\''+instr.instr+'\']');
        if (old_tr.length > 0) {
            tr.insertBefore(old_tr);
            old_tr.remove();
        } else {
            tbody.append(tr);
        }

        if (ui.isSelected(instr.instr)) {
            tr.addClass('active');
            if (ui.editState) {
                tr.children('td[data-col='+ui.selected.col+']').children('input').focus().select();
            } else {
                tr.focus();
            }
        }
    },

    handleKeyDown(e, instr) {
        var ui = this;
        var input = $(e.target);
        var index = Number.parseFloat(input.closest('td').attr('data-col'));
        var handled = true;
        switch(e.keyCode) {
            case 33: // page up
                var i = instr;
                var count = 5;
                for (var count = 5; count > 0; count--) {
                    if (i.prev) {
                        i = i.prev;
                    }
                }
                if (i != instr) {
                    ui.selectInstruction(i.instr, {row: i.instr, col: index});
                }
                break;
            case 34: // page down
                var i = instr;
                var count = 5;
                for (var count = 5; count > 0; count--) {
                    if (i.next) {
                        i = i.next;
                    }
                }
                if (i != instr) {
                    ui.selectInstruction(i.instr, {row: i.instr, col: index});
                }
                break;
            case 38: // up
                if (instr.prev) {
                    ui.selectInstruction(instr.prev.instr, {row: instr.prev.instr, col: index});
                }
                break;
            case 40: // down
                var next = instr.next;
                if (!next) {
                    ui.rally.addNextInstruction().then(function (row) {
                        ui.selectInstruction(row, {row: row, col: index});
                    });
                }
                if (instr.next) {
                    ui.selectInstruction(instr.next.instr, {row: instr.next.instr, col: index});
                }
                break;
            case 27: // escape
                ui.editMode(false);
                break;
            case 46: // delete
                if (e.shiftKey) {
                    if (instr.next) {
                        ui.selectInstruction(instr.next.instr, {row: instr.next.instr, col: index});
                    } else if (instr.prev) {
                        ui.selectInstruction(instr.prev.instr, {row: instr.prev.instr, col: index});
                    }
                    ui.editMode(false);
                    ui.rally.deleteInstruction(instr.instr);
                }
                break;
            default:
                handled = false;
                break;
        }
        if (handled) {
            e.stopPropagation();
            e.preventDefault();
        }
    },

    findRow: function(instr) {
        return $('tr[data-row=\''+instr+'\']');
    },

    findCell: function(instr, column) {
        return $('tr[data-row=\''+instr+'\']').children('td[data-col=\''+column+'\']');
    },

    isSelected: function(instr) {
        return (this.selected != null && this.selected.row == instr);
    },

    selectInstruction: function (instr, target) {
        $('tr.active').removeClass('active');
        var old_selected = this.selected;;
        instr = Number.parseFloat(instr);
        if (isNaN(instr)) {
            instr = null;
        }
        if (target) {
            target.row = Number.parseFloat(target.row);
            target.col = Number.parseInt(target.col);
        }
        if (old_selected != null) {
            var old_tr = $('tr[data-row=\''+old_selected.row+'\']');
            old_tr.find('input').trigger('blur');
        }
        if (instr != null) {
            if (target) {
                this.selected = target;
            } else {
                this.selected = {row: instr, col: 0};
            }
            this.renderInstruction(this.rally.instructions[instr]);
            if (old_selected != null) {
                this.renderInstruction(this.rally.instructions[old_selected.row]);
            }
            var tr = $('tr[data-row=\''+instr+'\']');
            if (this.editState && target) {
                var td = this.findCell(target.row, target.col);
                var input = td.find('input');
                input.focus();
                input.select();
            }
            tr.addClass('active');
        }
    },

    editMode: function (state, target) {
        if (typeof state != 'undefined') {
            this.editState = state;
        }
        if (this.selected != null) {
            this.renderInstruction(this.rally.instructions[this.selected.row]);
            if (this.editState && target) {
                var tr = this.findRow(target.row);
                var td = this.findCell(target.row, target.col);
                var input = td.find('input');
                input.focus();
                input.select();
            }
        }
        var editButton = $('#edit-instruction');
        if (state) {
            editButton.addClass('active');
        } else {
            editButton.removeClass('active');
        }
    },
};
<|endoftext|>


<commit_before>/**
 * @author bocelli.hu
 * @since tupai.js 0.1
 */
var path = require('path');
var fs = require('fs');
var spawn = require('child_process').spawn;
var http = require('http');
var ejs = require('ejs');
var express = require('express');
var httpProxy = require('http-proxy');
var tupai = require(__dirname);

var tupaijsPath = path.join(__dirname, '..', '..', 'bin', 'tupaijs');
function execute(args, callback) {
    var cp = spawn(tupaijsPath, args);

    cp.stderr.pipe(process.stderr);
    if(callback) {
        var result='';
        cp.stdout.on('data', function(data) {
            result += data.toString();
        });
        cp.on('close', function(code) {
            callback(result);
        });
    } else {
        if(mConfig.logTupai) {
            cp.stdout.pipe(process.stdout);
        }
    }
    return cp;
}

function genConfigs() {
    console.log('gen configs:');
    tupai.compileConfigSync(mConfig.configs, mConfig.genConfigs, 'Config');
}

function genTemplates(callback) {
    console.log('gen template files:');
    console.log('    ' + mConfig.templates + ' -> ' + mConfig.genTemplates);
    execute(['templates', mConfig.templates, mConfig.genTemplates, '-m', 'no'])
    .on('exit', function() {
        callback();
    });
}

function genTemplate(filePath, packageName, callback) {
    console.log('gen template file:' + filePath);
    execute(['template', filePath, mConfig.genTemplates, packageName, '-m', 'no'])
    .on('exit', function() {
        callback();
    });
}

function listTupaiClass(callback) {
    var packagejsHtml = '';
    var tupaiSrcDir = path.join(__dirname, '..', '..', 'src', 'tupai');
    execute(['list', '--classPath', tupaiSrcDir, '--ignoreNotFound'], function(output) {
        //if (!output) {return;}
        var classes = JSON.parse(output);
        //console.log(classes);
        var tupaijsfiles = classes.map(function(cls){
            var path = cls.path;
            return path.replace(tupaiSrcDir + "/", "");
        });

        var scripts = tupaijsfiles.map(function(filename){ return '';});
        tupaiFilesHtml = packagejsHtml + "\n" + scripts.join("\n");

        callback();
    });

}

function listClass(callback) {
    var classPath = mConfig.genConfigs + ';' + mConfig.genTemplates + ';' + mConfig.sources;
    execute(['list', '--classPath', classPath, '--ignoreNotFound'], function(output) {
        var classList;
        try {
            classList = JSON.parse(output);
        } catch(e) {
            console.log("can't parse classlist. ");
            console.log(output);
        }

        if(classList) {
            callback(classList);
        }
    });
}

var URL = require('url');
function createProxyHttpHandler(config) {
    return (function(req, res, next) {
        req.url = config.basePathName + req.url;
        config.proxy.proxyRequest(req, res)
    });
}

function useProxyRequest(app) {

    var serverConfig = mConfig.server;
    if(!serverConfig.proxies) return;
    if(typeof serverConfig.proxies !== 'object') return;

    var proxies = serverConfig.proxies;
    console.log('proxies: ');
    for(var name in proxies) {
        var u = URL.parse(proxies[name]);
        var config = {
            proxy: new httpProxy.HttpProxy({
                target: {
                    host: u.hostname,
                    port: u.port,
                    https: u.protocol === 'https:'
                }
            }),
            basePathName: (u.pathname.length > 1 ? u.pathname : '')
        };

        console.log('    ' + name + ' -> ' + u.protocol + '//' +
                    u.hostname + ':' + (u.port?u.port:80) + u.pathname);
        app.use(name, createProxyHttpHandler(config));
    }
}

function sendFile(router) {
    return function(req, res) {

        var filename;
        var renderTemplate;
        if(typeof router === 'string') {
            filename = router;
            renderTemplate = false;
        } else if(typeof router === 'object'){
            filename = router.path;
            renderTemplate = router.template;
        }

        if(!filename) {
            res.send(404);
            return;
        }

        if(req.params.length > 0) {
            filename = tupai.format(filename, req.params);
            if(!fs.existsSync(filename)) {
                res.send(404);
                return;
            }
            var stat = fs.statSync(filename);
            if(stat.isDirectory()) {
                // TODO  to serve files, providing a full-featured file browser
            }
        }

        if(renderTemplate) {
            var params = {};
            [
                'params', 'query', 'body', 'cookies',
                'ip', 'path', 'host', 'protocol', 'originalUrl'
            ].forEach(function(name) {
                params[name] = req[name];
            })
            ejs.renderFile(filename, params, function(err, data) {
                if(err) {
                    res.send(500, err);
                } else {
                    res.send(data);
                }
            });
        } else {
            res.sendfile(filename);
        }
    };
}

function useRouters(app) {

    var serverConfig = mConfig.server;
    var routers = serverConfig.routers;
    if(!routers) return;
    if(typeof routers !== 'object') return;

    console.log('routers: ');
    for(var name in routers) {
        var router = routers[name];

        console.log('    ' + name + ' -> ' + router);
        app.get(name, sendFile(router));
    }
}

function configDebugMode(app) {
    var serverConfig = mConfig.server;
    var webDir = path.join(__dirname, '..', '..', 'releases', mConfig.web);
    //console.log(mConfig);

    useProxyRequest(app);
    app.use('/__js', express.static(mConfig.sources));
    app.use('/__js', express.static(mConfig.genTemplates));
    app.use('/__js', express.static(mConfig.genConfigs));
    app.use('/', function(req, res, next) {
        var p = req.path;
        if(p === '/') p = '/index.html';
        if(p.match(/^\/.*\.html$/)) {
            var filePath = path.join(mConfig.web, p);
            if(!fs.existsSync(filePath)) {
                next();
            } else {
                var content = fs.readFileSync(filePath).toString();
                content = content.replace(/[\s\S]*/, classListHtml);
                content = content.replace(/[\s\S]*/, tupaiFilesHtml);
                res.send(content);
            }
        } else if(p == '/js/tupai.min.js') {
            /*if(fs.existsSync(path.join(mConfig.web,'js','tupai.min.js'))) {
                next();
            } else {*/
                // return unminified javascript in debug mode.
                var content = fs.readFileSync(path.join(webDir, 'tupai-last.js')).toString();
                res.send(content);
            //}
        } else {
            next();
        }
    });

    var tupaiRootDir = path.join(__dirname, '..', '..');
    app.use('/__tupairoot', express.static(tupaiRootDir));

    app.use('/tupai', express.static(webDir));
    app.use('/tupai', express.directory(webDir));
    app.use('/templates', express.static(mConfig.templates));
    app.use('/templates', express.directory(mConfig.templates));
}

function startHttpServer(releaseMode, options) {
    var app = express();
    app.use(express.favicon(path.join(__dirname, '..', '..', 'favicon.ico')));
    if(options.printLog) {
        app.use(express.logger());
    }

    var serverConfig = mConfig.server;
    useRouters(app);
    if(!releaseMode) {
        configDebugMode(app, serverConfig);
    }
    app.use('/', express.static(mConfig.web));

    var port = serverConfig.port;
    http.createServer(app).listen(port, function() {
        console.log('\nopen below url in your browser.');
        console.log('http://localhost:' + port);
    });
}

var classListHtml;
var tupaiFilesHtml;

function renderClassListHtml(callback) {
    listClass(function(cl) {
        //classListHtml = '';
        classListHtml = '';
        cl.forEach(function(classzz) {
            classListHtml += '\n';
        });
        callback && callback();
    });
}

var chokidar = require('chokidar');
function watchFs() {
    console.log('start watching files:');

    var paths = [mConfig.sources, mConfig.templates, mConfig.configs];
    paths.forEach(function(path) {
        console.log('    watching ' + path);
    });

    var renderClassListHtmlFn = function(changeType) {
        // TODO when change js file to add use will not render class list.
        if(changeType !== 'change') {
            renderClassListHtml();
        }
    };
    var rules = [
        {
            regexp: new RegExp(mConfig.sources + "\/.*\.js$"),
            fn: function(changeType, filePath) {
                renderClassListHtmlFn(changeType);
            }
        },
        {
            regexp: new RegExp(mConfig.templates + "\/.*\.html$"),
            fn: function(changeType, filePath) {
                if(changeType === 'unlink') {
                    fs.unlinkSync(path.join('gen', filePath.replace(/\.html$/, '.js')));
                    renderClassListHtmlFn(changeType);
                } else {
                    var classPath = path.relative(mConfig.templates, filePath).replace(/\.html$/, '');
                    genTemplate(filePath, classPath.split('/').join('.'), function() {
                        renderClassListHtmlFn(changeType);
                    });
                }
            }
        },
        {
            regexp: new RegExp(mConfig.configs + "\/.*\.json$"),
            fn: function(changeType, filePath) {
                try {
                    genConfigs();
                } catch(e) {
                    console.error('can\'t generate configs', e);
                }
            }
        }
    ];

    var changeFn = function(changeType, filePath) {
        console.log(changeType + ':' + filePath);
        rules.forEach(function(rule) {
            if(filePath.match(rule.regexp)) {
                rule.fn(changeType, filePath);
            }
        });
    };

    var ignoredFn = function(path, st) {
        if(/\/\./.test(path)) return true;
        if(/\/.*~$/.test(path)) return true;
        return false;
    };
    var watcher = chokidar.watch(paths, {ignored: ignoredFn, persistent: true, ignoreInitial: true});
    ['add', 'change', 'unlink'].forEach(function(changeType) {
        watcher.on(changeType, function(filePath) {
            changeFn(changeType, filePath);
        });
    });
    watcher.on('error', function(error) {
        console.error('Error happened', error);
    });
}

var mConfig;
exports.start = function(options) {
    if(!tupai.isTupaiProjectDir()) {
        console.error('current dir is not a tupai project dir.');
        return;
    }
    mConfig = tupai.getConfig();
    if(options.serverConfig) {
        var filename = options.serverConfig;
        if(!fs.existsSync(filename)) {
            console.error(filename + ' not exists.');
        } else {
            try {
                var configData = fs.readFileSync(filename);
                var data = JSON.parse(configData.toString());
                mConfig.server = data;
            } catch(e) {
                console.error(e);
            }
        }
    }
    var target = (options && options.target) || 'debug';
    var releaseMode = ('release' === target);
    if(releaseMode) {
        startHttpServer(releaseMode, options);
    } else {
        tupai.rmdirSync('gen');
        fs.mkdirSync('gen');
        genConfigs();
        genTemplates(function() {
            listTupaiClass(function(){
                renderClassListHtml(function() {
                    watchFs();
                    startHttpServer(releaseMode, options);
                });
            });
        });
    }
}

<commit_msg>integrate 2 map calls into one<commit_after>/**
 * @author bocelli.hu
 * @since tupai.js 0.1
 */
var path = require('path');
var fs = require('fs');
var spawn = require('child_process').spawn;
var http = require('http');
var ejs = require('ejs');
var express = require('express');
var httpProxy = require('http-proxy');
var tupai = require(__dirname);

var tupaijsPath = path.join(__dirname, '..', '..', 'bin', 'tupaijs');
function execute(args, callback) {
    var cp = spawn(tupaijsPath, args);

    cp.stderr.pipe(process.stderr);
    if(callback) {
        var result='';
        cp.stdout.on('data', function(data) {
            result += data.toString();
        });
        cp.on('close', function(code) {
            callback(result);
        });
    } else {
        if(mConfig.logTupai) {
            cp.stdout.pipe(process.stdout);
        }
    }
    return cp;
}

function genConfigs() {
    console.log('gen configs:');
    tupai.compileConfigSync(mConfig.configs, mConfig.genConfigs, 'Config');
}

function genTemplates(callback) {
    console.log('gen template files:');
    console.log('    ' + mConfig.templates + ' -> ' + mConfig.genTemplates);
    execute(['templates', mConfig.templates, mConfig.genTemplates, '-m', 'no'])
    .on('exit', function() {
        callback();
    });
}

function genTemplate(filePath, packageName, callback) {
    console.log('gen template file:' + filePath);
    execute(['template', filePath, mConfig.genTemplates, packageName, '-m', 'no'])
    .on('exit', function() {
        callback();
    });
}

function listTupaiClass(callback) {
    var packagejsHtml = '';
    var tupaiSrcDir = path.join(__dirname, '..', '..', 'src', 'tupai');
    execute(['list', '--classPath', tupaiSrcDir, '--ignoreNotFound'], function(output) {
        //if (!output) {return;}
        var classes = JSON.parse(output);
        //console.log(classes);
        var scripts = classes.map(function(cls){
            var path = cls.path;
            var filename = path.replace(tupaiSrcDir + "/", "");
            return '';
        });

        tupaiFilesHtml = packagejsHtml + "\n" + scripts.join("\n");

        callback();
    });

}

function listClass(callback) {
    var classPath = mConfig.genConfigs + ';' + mConfig.genTemplates + ';' + mConfig.sources;
    execute(['list', '--classPath', classPath, '--ignoreNotFound'], function(output) {
        var classList;
        try {
            classList = JSON.parse(output);
        } catch(e) {
            console.log("can't parse classlist. ");
            console.log(output);
        }

        if(classList) {
            callback(classList);
        }
    });
}

var URL = require('url');
function createProxyHttpHandler(config) {
    return (function(req, res, next) {
        req.url = config.basePathName + req.url;
        config.proxy.proxyRequest(req, res)
    });
}

function useProxyRequest(app) {

    var serverConfig = mConfig.server;
    if(!serverConfig.proxies) return;
    if(typeof serverConfig.proxies !== 'object') return;

    var proxies = serverConfig.proxies;
    console.log('proxies: ');
    for(var name in proxies) {
        var u = URL.parse(proxies[name]);
        var config = {
            proxy: new httpProxy.HttpProxy({
                target: {
                    host: u.hostname,
                    port: u.port,
                    https: u.protocol === 'https:'
                }
            }),
            basePathName: (u.pathname.length > 1 ? u.pathname : '')
        };

        console.log('    ' + name + ' -> ' + u.protocol + '//' +
                    u.hostname + ':' + (u.port?u.port:80) + u.pathname);
        app.use(name, createProxyHttpHandler(config));
    }
}

function sendFile(router) {
    return function(req, res) {

        var filename;
        var renderTemplate;
        if(typeof router === 'string') {
            filename = router;
            renderTemplate = false;
        } else if(typeof router === 'object'){
            filename = router.path;
            renderTemplate = router.template;
        }

        if(!filename) {
            res.send(404);
            return;
        }

        if(req.params.length > 0) {
            filename = tupai.format(filename, req.params);
            if(!fs.existsSync(filename)) {
                res.send(404);
                return;
            }
            var stat = fs.statSync(filename);
            if(stat.isDirectory()) {
                // TODO  to serve files, providing a full-featured file browser
            }
        }

        if(renderTemplate) {
            var params = {};
            [
                'params', 'query', 'body', 'cookies',
                'ip', 'path', 'host', 'protocol', 'originalUrl'
            ].forEach(function(name) {
                params[name] = req[name];
            })
            ejs.renderFile(filename, params, function(err, data) {
                if(err) {
                    res.send(500, err);
                } else {
                    res.send(data);
                }
            });
        } else {
            res.sendfile(filename);
        }
    };
}

function useRouters(app) {

    var serverConfig = mConfig.server;
    var routers = serverConfig.routers;
    if(!routers) return;
    if(typeof routers !== 'object') return;

    console.log('routers: ');
    for(var name in routers) {
        var router = routers[name];

        console.log('    ' + name + ' -> ' + router);
        app.get(name, sendFile(router));
    }
}

function configDebugMode(app) {
    var serverConfig = mConfig.server;
    var webDir = path.join(__dirname, '..', '..', 'releases', mConfig.web);
    //console.log(mConfig);

    useProxyRequest(app);
    app.use('/__js', express.static(mConfig.sources));
    app.use('/__js', express.static(mConfig.genTemplates));
    app.use('/__js', express.static(mConfig.genConfigs));
    app.use('/', function(req, res, next) {
        var p = req.path;
        if(p === '/') p = '/index.html';
        if(p.match(/^\/.*\.html$/)) {
            var filePath = path.join(mConfig.web, p);
            if(!fs.existsSync(filePath)) {
                next();
            } else {
                var content = fs.readFileSync(filePath).toString();
                content = content.replace(/[\s\S]*/, classListHtml);
                content = content.replace(/[\s\S]*/, tupaiFilesHtml);
                res.send(content);
            }
        } else if(p == '/js/tupai.min.js') {
            /*if(fs.existsSync(path.join(mConfig.web,'js','tupai.min.js'))) {
                next();
            } else {*/
                // return unminified javascript in debug mode.
                var content = fs.readFileSync(path.join(webDir, 'tupai-last.js')).toString();
                res.send(content);
            //}
        } else {
            next();
        }
    });

    var tupaiRootDir = path.join(__dirname, '..', '..');
    app.use('/__tupairoot', express.static(tupaiRootDir));

    app.use('/tupai', express.static(webDir));
    app.use('/tupai', express.directory(webDir));
    app.use('/templates', express.static(mConfig.templates));
    app.use('/templates', express.directory(mConfig.templates));
}

function startHttpServer(releaseMode, options) {
    var app = express();
    app.use(express.favicon(path.join(__dirname, '..', '..', 'favicon.ico')));
    if(options.printLog) {
        app.use(express.logger());
    }

    var serverConfig = mConfig.server;
    useRouters(app);
    if(!releaseMode) {
        configDebugMode(app, serverConfig);
    }
    app.use('/', express.static(mConfig.web));

    var port = serverConfig.port;
    http.createServer(app).listen(port, function() {
        console.log('\nopen below url in your browser.');
        console.log('http://localhost:' + port);
    });
}

var classListHtml;
var tupaiFilesHtml;

function renderClassListHtml(callback) {
    listClass(function(cl) {
        //classListHtml = '';
        classListHtml = '';
        cl.forEach(function(classzz) {
            classListHtml += '\n';
        });
        callback && callback();
    });
}

var chokidar = require('chokidar');
function watchFs() {
    console.log('start watching files:');

    var paths = [mConfig.sources, mConfig.templates, mConfig.configs];
    paths.forEach(function(path) {
        console.log('    watching ' + path);
    });

    var renderClassListHtmlFn = function(changeType) {
        // TODO when change js file to add use will not render class list.
        if(changeType !== 'change') {
            renderClassListHtml();
        }
    };
    var rules = [
        {
            regexp: new RegExp(mConfig.sources + "\/.*\.js$"),
            fn: function(changeType, filePath) {
                renderClassListHtmlFn(changeType);
            }
        },
        {
            regexp: new RegExp(mConfig.templates + "\/.*\.html$"),
            fn: function(changeType, filePath) {
                if(changeType === 'unlink') {
                    fs.unlinkSync(path.join('gen', filePath.replace(/\.html$/, '.js')));
                    renderClassListHtmlFn(changeType);
                } else {
                    var classPath = path.relative(mConfig.templates, filePath).replace(/\.html$/, '');
                    genTemplate(filePath, classPath.split('/').join('.'), function() {
                        renderClassListHtmlFn(changeType);
                    });
                }
            }
        },
        {
            regexp: new RegExp(mConfig.configs + "\/.*\.json$"),
            fn: function(changeType, filePath) {
                try {
                    genConfigs();
                } catch(e) {
                    console.error('can\'t generate configs', e);
                }
            }
        }
    ];

    var changeFn = function(changeType, filePath) {
        console.log(changeType + ':' + filePath);
        rules.forEach(function(rule) {
            if(filePath.match(rule.regexp)) {
                rule.fn(changeType, filePath);
            }
        });
    };

    var ignoredFn = function(path, st) {
        if(/\/\./.test(path)) return true;
        if(/\/.*~$/.test(path)) return true;
        return false;
    };
    var watcher = chokidar.watch(paths, {ignored: ignoredFn, persistent: true, ignoreInitial: true});
    ['add', 'change', 'unlink'].forEach(function(changeType) {
        watcher.on(changeType, function(filePath) {
            changeFn(changeType, filePath);
        });
    });
    watcher.on('error', function(error) {
        console.error('Error happened', error);
    });
}

var mConfig;
exports.start = function(options) {
    if(!tupai.isTupaiProjectDir()) {
        console.error('current dir is not a tupai project dir.');
        return;
    }
    mConfig = tupai.getConfig();
    if(options.serverConfig) {
        var filename = options.serverConfig;
        if(!fs.existsSync(filename)) {
            console.error(filename + ' not exists.');
        } else {
            try {
                var configData = fs.readFileSync(filename);
                var data = JSON.parse(configData.toString());
                mConfig.server = data;
            } catch(e) {
                console.error(e);
            }
        }
    }
    var target = (options && options.target) || 'debug';
    var releaseMode = ('release' === target);
    if(releaseMode) {
        startHttpServer(releaseMode, options);
    } else {
        tupai.rmdirSync('gen');
        fs.mkdirSync('gen');
        genConfigs();
        genTemplates(function() {
            listTupaiClass(function(){
                renderClassListHtml(function() {
                    watchFs();
                    startHttpServer(releaseMode, options);
                });
            });
        });
    }
}

<|endoftext|>


<commit_before>const fs = require('fs');
const http = require('http');
const shell = require('shelljs');
const decompress = require('decompress');
const decompressTargz = require('decompress-targz');
const env = require('./env.js');

function download(url, destination) {
    if (fs.existsSync(destination)) {
        shell.echo(`${destination} already exists. Skipping download.`);

        return Promise.resolve();
    }

    const file = fs.createWriteStream(destination);

    return new Promise((resolve, reject) => {
        const req = http.get(`${env.ZK_URL}`, (res) => {
            res.on('data', function (chunk) {
                file.write(chunk);
            });

            res.on('end', function () {
                file.end();
                resolve();
            })
        });

        req.on('error', (e) => {
            file.unlink(destination);
            reject(e);
        });

        file.on('error', () => {
            file.unlink(destination);
            reject(new Error('File error'));
        });
    });
}

shell.cd(env.DEPS);

download(env.ZK_URL, env.ZK_FILE)
    .then(() => {
        shell.rm('-rf', env.ZK_DEPS);

        decompress(env.ZK_FILE, './', {
            plugins: [
                decompressTargz()
            ]
        }).then(() => {
            shell.mv(env.ZK, env.ZK_DEPS);
        });

    })
    .catch((e) => {
        shell.echo(`Unable to download zookeeper library. Error: ${e.message}`);
        shell.exit(1);
    });<commit_msg>refactor: extract function<commit_after>const fs = require('fs');
const http = require('http');
const shell = require('shelljs');
const decompress = require('decompress');
const decompressTargz = require('decompress-targz');
const env = require('./env.js');

function writeFileExecutor(url, destination, resolve, reject) {
    const file = fs.createWriteStream(destination);

    const req = http.get(url, (res) => {
        res.on('data', function (chunk) {
            file.write(chunk);
        });

        res.on('end', function () {
            file.end();
            resolve();
        })
    });

    req.on('error', (e) => {
        file.unlink(destination);
        reject(e);
    });

    file.on('error', () => {
        file.unlink(destination);
        reject(new Error('File error'));
    });
}

function download(url, destination) {
    if (fs.existsSync(destination)) {
        shell.echo(`${destination} already exists. Skipping download.`);

        return Promise.resolve();
    }

    const executor = writeFileExecutor.bind(null, url, destination);
    return new Promise(executor);
}

shell.cd(env.DEPS);

download(env.ZK_URL, env.ZK_FILE)
    .then(() => {
        shell.rm('-rf', env.ZK_DEPS);

        decompress(env.ZK_FILE, './', {
            plugins: [
                decompressTargz()
            ]
        }).then(() => {
            shell.mv(env.ZK, env.ZK_DEPS);
        });

    })
    .catch((e) => {
        shell.echo(`Unable to download zookeeper library. Error: ${e.message}`);
        shell.exit(1);
    });<|endoftext|>


<commit_before>const readFileSync = require('fs').readFileSync;
const execSync = require('child_process').execSync;
const inInstall = require('in-publish').inInstall;
const prettyBytes = require('pretty-bytes');
const gzipSize = require('gzip-size');
const packageJson = require('../package.json');

const bowerRepo = 'https://github.com/featureflow/featureflow-client-bower.git';
const bowerTempPath = '_bower';

if (inInstall()) {
  process.exit(0);
}

function exec(command) {
  execSync(command, { stdio: 'inherit' });
}

exec('npm run build');

exec('rm -rf '+bowerTempPath);
exec('git clone '+bowerRepo+' '+bowerTempPath);
exec('cp dist/* '+bowerTempPath);
exec('cd '+bowerTempPath+' ' +
  '&& git add . ' +
  '&& git commit -m \"v'+packageJson.version+'" ' +
  '&& git tag '+packageJson.version+' ' +
  '&& git push origin master' +
  '&& rm -rf '+bowerTempPath);

<commit_msg>Push tags with bower release.<commit_after>const readFileSync = require('fs').readFileSync;
const execSync = require('child_process').execSync;
const inInstall = require('in-publish').inInstall;
const prettyBytes = require('pretty-bytes');
const gzipSize = require('gzip-size');
const packageJson = require('../package.json');

const bowerRepo = 'https://github.com/featureflow/featureflow-client-bower.git';
const bowerTempPath = '_bower';

if (inInstall()) {
  process.exit(0);
}

function exec(command) {
  execSync(command, { stdio: 'inherit' });
}

exec('npm run build');

exec('rm -rf '+bowerTempPath);
exec('git clone '+bowerRepo+' '+bowerTempPath);
exec('cp dist/* '+bowerTempPath);
exec('cd '+bowerTempPath+' ' +
  '&& git add . ' +
  '&& git commit -m \"v'+packageJson.version+'" ' +
  '&& git tag '+packageJson.version+' ' +
  '&& git push origin master --tags' +
  '&& rm -rf '+bowerTempPath);

<|endoftext|>


<commit_before>// Native modules
var fs = require('fs');
var path = require('path');

// External modules
var _ = require('lodash');
var glob = require("glob");
var chalk = require('chalk');

// Basic config
var config = require('./_config');
var args = process.argv.slice(2);
var baseProjectPath = path.join(__dirname, '../');
var baseTemplatePath = path.join(__dirname, '/templates');
var type = args[0];

// Test if args are passed?
if (1 >= args.length) {
	console.log(chalk.red('You need to give the module a name'));
	console.log(chalk.yellow('npm run add:' + type + ' NAME'));
	return;
}

var dataObj = {
	name: args[1],
	nameUpper: capitalizeFirstLetter(args[1]),
	nameLower: args[1].toLowerCase(),
	nameSlugified: slugify(args[1])
};

// Setup based on config
if (!_.isUndefined(config.interpolate)) {
	_.templateSettings.interpolate = config.interpolate;
}

var module = config.modules.filter(function (obj) {
    return obj.type === type;
})[0];

build(module, function(filesPath, outputPath) {
	var arrLength = filesPath.length;

	_.map(filesPath, function(filePath, i) {
		var pathParts = filePath.split('/');
		var pathEnding = pathParts[pathParts.length - 1];
		var n = pathEnding.indexOf('.');
		var resultEnding = pathEnding.substring(n);

		read(filePath, function(fileContent) {
			var moduleDirName = type === 'constant' ? path.join(__dirname, '../', outputPath) : path.join(__dirname, '../', outputPath, dataObj.name);
			var setPath = resultEnding === '.scss' ?  path.join(moduleDirName, '_' + dataObj.name + resultEnding) : path.join(moduleDirName, dataObj.name + resultEnding);

			if (!fs.existsSync(moduleDirName) && type !== 'constant'){
			    fs.mkdirSync(moduleDirName);
			}

			write(setPath, _.template(fileContent)(dataObj), function() {
				// Only show logs on the finish of the last file
				if (arrLength - 1 === i) {
					console.log(chalk.green('The new module has been created'));
					console.log(chalk.bold.green('the module name is: ' + type + '.' + dataObj.name));
					console.log('-------------------------------');

					if (type === 'controller') {
						console.log(chalk.yellow('Remember to bind the sass file in app.scss in the root application folder'));
						console.log(chalk.yellow('And Bind your controller in app.js'));
					} else if (type === 'component') {
						console.log(chalk.yellow('Remember to bind the sass file in app.scss in the root application folder'));
						console.log(chalk.yellow('And add this component as a dependency in the controller you need it'));
					} else {
						console.log(chalk.yellow('Remember to bind the dependency where ever you need this module'));
					}
				}
			});
		});
	});
});

function build(module, callback) {
	var modulePath = path.join(baseTemplatePath, module.type);
	var moduleFilesGlob = modulePath + '/*.' + module.fileTypes;

	glob(moduleFilesGlob, function (err, filesPath) {
		if (err) {
			return console.log(chalk.red(err));
		}

		callback(filesPath, module.output);
	});
}

function read(path, cb) {
	fs.readFile(path, 'utf8', function(err, data) {
		if (err) {
			return console.log(chalk.red(err));
		}

		cb(data);
	});
}

function write(path, content, cb) {
	fs.writeFile(path, content, function (err,data) {
	  if (err) {
	    return console.log(chalk.red(err));
	  }

	  cb(data);
	});
}

function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}

function slugify(text) {
  return text.toString().toLowerCase()
    .replace(/\s+/g, '-')           // Replace spaces with -
    .replace(/[^\w\-]+/g, '')       // Remove all non-word chars
    .replace(/\-\-+/g, '-')         // Replace multiple - with single -
    .replace(/^-+/, '')             // Trim - from start of text
    .replace(/-+$/, '');            // Trim - from end of text
}
<commit_msg>Add checks for module/file exists so it wont overwrite if it exists<commit_after>// Native modules
var fs = require('fs');
var path = require('path');

// External modules
var _ = require('lodash');
var glob = require("glob");
var chalk = require('chalk');

// Basic config
var config = require('./_config');
var args = process.argv.slice(2);
var baseProjectPath = path.join(__dirname, '../');
var baseTemplatePath = path.join(__dirname, '/templates');
var type = args[0];

// Test if args are passed?
if (1 >= args.length) {
	console.log(chalk.red('You need to give the module a name'));
	console.log(chalk.yellow('npm run add:' + type + ' NAME'));
	return;
}

var dataObj = {
	name: args[1],
	nameUpper: capitalizeFirstLetter(args[1]),
	nameLower: args[1].toLowerCase(),
	nameSlugified: slugify(args[1])
};

// Setup based on config
if (!_.isUndefined(config.interpolate)) {
	_.templateSettings.interpolate = config.interpolate;
}

var module = config.modules.filter(function (obj) {
    return obj.type === type;
})[0];

build(module, function(filesPath, outputPath) {
	var arrLength = filesPath.length;
	var moduleDirName = type === 'constant' ? path.join(__dirname, '../', outputPath) : path.join(__dirname, '../', outputPath, dataObj.name);

	if (!fs.existsSync(moduleDirName) && type !== 'constant'){
	    fs.mkdirSync(moduleDirName);
	} else if (type !== 'constant') {
		console.log(chalk.red('The module with that name already exists'));
		console.log(chalk.red('Please select another one'));
		console.log('-------------------------------');
		console.log(chalk.yellow.bold('FolderPath'));
		console.log(chalk.yellow(moduleDirName));
		return;
	}

	_.map(filesPath, function(filePath, i) {
		var pathParts = filePath.split('/');
		var pathEnding = pathParts[pathParts.length - 1];
		var n = pathEnding.indexOf('.');
		var resultEnding = pathEnding.substring(n);

		read(filePath, function(fileContent) {
			var setPath = resultEnding === '.scss' ?  path.join(moduleDirName, '_' + dataObj.name + resultEnding) : path.join(moduleDirName, dataObj.name + resultEnding);

			// Check if the file exists (should only be constants)
			if (fs.existsSync(setPath)) {
				console.log(chalk.red('A file with this name do already exist'));
				console.log(chalk.red('Please select another one'));
				console.log('-------------------------------');
				console.log(chalk.yellow.bold('Filepath'));
				console.log(chalk.yellow(setPath));
			    return;
			}

			write(setPath, _.template(fileContent)(dataObj), function() {
				// Only show logs on the finish of the last file
				if (arrLength - 1 === i) {
					console.log(chalk.green('The new module has been created'));
					console.log(chalk.bold.green('the module name is: ' + type + '.' + dataObj.name));
					console.log('-------------------------------');

					if (type === 'controller') {
						console.log(chalk.yellow('Remember to bind the sass file in app.scss in the root application folder'));
						console.log(chalk.yellow('And Bind your controller in app.js'));
					} else if (type === 'component') {
						console.log(chalk.yellow('Remember to bind the sass file in app.scss in the root application folder'));
						console.log(chalk.yellow('And add this component as a dependency in the controller you need it'));
					} else {
						console.log(chalk.yellow('Remember to bind the dependency where ever you need this module'));
					}
				}
			});
		});
	});
});

function build(module, callback) {
	var modulePath = path.join(baseTemplatePath, module.type);
	var moduleFilesGlob = modulePath + '/*.' + module.fileTypes;

	glob(moduleFilesGlob, function (err, filesPath) {
		if (err) {
			return console.log(chalk.red(err));
		}

		callback(filesPath, module.output);
	});
}

function read(path, cb) {
	fs.readFile(path, 'utf8', function(err, data) {
		if (err) {
			return console.log(chalk.red(err));
		}

		cb(data);
	});
}

function write(path, content, cb) {
	fs.writeFile(path, content, function (err,data) {
	  if (err) {
	    return console.log(chalk.red(err));
	  }

	  cb(data);
	});
}

function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}

function slugify(text) {
    return text.replace( /([a-z])([A-Z])/g, '$1-$2' ).toLowerCase();
}
<|endoftext|>


<commit_before>/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import mitt from 'mitt';
import Unobservable from './utils/Unobservable';
import Uuid from './utils/uuid';

/**
 * Used for caching resources. Requires the resource to have an unique id field
 * @see {@link PaginatedResourceWrapper}
 * @todo Add periodic data refreshing while idle, most likely implemented in cache
 */
export default class ResourceCache extends Unobservable {
  constructor(api, cacheTime = api.defaults.cacheSeconds, dereference = api.defaults.dereferenceCache) {
    super();

    this._api = api;
    this.cacheTime = cacheTime;
    this.dereference = dereference;
    this.emitter = mitt();

    this._storage = {};
  }

  /**
   * Push a page into the cache
   * @param {PaginatedResourceListing} page - Data to be cached
   * @param {boolean} [diff=false] - Differential result used for updating the cache. Cache entry won't be used to for key dropping.
   * @returns {void}
   * @todo diff documentation
   */
  push(page, diff = false) {
    if (page.rows === 0) {
      return; // Don't insert empty pages
    }

    delete page.__ob__; // Remove VueJs observer

    // Test if this is data we can actually work with by testing if there are any non-numeric ids (undefined etc)
    const invalidData = page.data.map(row => row.id).filter(x => typeof x !== 'number').length > 0;

    if (invalidData) {
      throw new TypeError('Missing or invalid row.id for page.data. Data rows must to contain a numeric "id" field.');
    }

    const validThrough = this._timestamp + this.cacheTime;
    const cacheId = Uuid.uuid4();
    const data = {
      page, validThrough, diff,
      id: cacheId,
      timeout: setTimeout(
        () => this._deleteCacheIds(cacheId),
        this.cacheTime * 1000,
      ),
    };

    const storage = this._storage[page.route] || (this._storage[page.route] = {});

    (storage[page.cacheToken] || (storage[page.cacheToken] = [])).push(data);

    this.emitter.emit('push', {page, validThrough, resourceUrl: page.route});
    this.emitter.emit('invalidate', {resourceUrl: page.route});
  }

  /**
   * Delete from cache using cacheId
   * @param {String|Array<String>} ids - cache ids
   * @returns {void}
   */
  _deleteCacheIds(ids) {
    if (!(ids instanceof Array)) {
      this._deleteCacheIds([ids]);
      return;
    }

    let found = 0;

    for (const resourceUrl of Object.keys(this._storage)) {
      for (const token of Object.keys(this._storage[resourceUrl])) {
        const entries = this._storage[resourceUrl][token];

        for (let i = 0; i < entries.length; i++) {
          if (ids.includes(entries[i].id)) {
            entries.splice(i, 1);
            i--;
            found++;

            if (found === ids.length) {
              return;
            }
          }
        }
      }
    }
  }

  /**
   * Revalidate all data and delete stale data
   * @param {String} resourceUrl - Resource url
   * @returns {void}
   */
  revalidate(resourceUrl = null) {
    if (!resourceUrl) {
      Object.keys(this._storage).map(this.revalidate);
    } else if (this._storage[resourceUrl]) {
      const storage = this._storage[resourceUrl];

      // Remove old data from the cache and stop old timeouts
      Object.keys(storage).forEach(key => {
        storage[key]
          .filter(row => row.validThrough < this._timestamp)
          .forEach(row => clearTimeout(row.timeout));

        storage[key] = storage[key].filter(row => row.validThrough >= this._timestamp);
      });

      const junk = Object.keys(storage).filter(key => storage[key].length === 0);

      // Delete empty
      junk.forEach(key => delete storage[key]);
      if (Object.keys(storage).length === 0) {
        delete this._storage[resourceUrl];
      }

      if (junk.length > 0) {
        this.emitter.emit('invalidate', {resourceUrl});
      }
    }
  }

  /**
   * Collect relevant cached pages
   * @param {String} resourceUrl - resource url
   * @param {String} cacheToken - Cache token
   * @see {@link PaginatedResourceListing#cacheToken}
   * @returns {Array<PaginatedResourceListing>} - Relevant cached pages
   */
  collectPages(resourceUrl, cacheToken = '') {
    cacheToken = cacheToken.toLowerCase();

    // Storage array or []
    const storage = (this._storage[resourceUrl] || {})[cacheToken] || [];

    // Sort by validThrough and extract pages
    return storage.sort((a, b) => a.validThrough - b.validThrough);
  }

  /**
   * Clears the cache
   * @param {String} resourceUrl - Resource url
   * @returns {void}
   */
  clear(resourceUrl = '') {
    if (!resourceUrl) {
      Object.keys(this._storage).forEach(url => {
        this.emitter.emit('invalidate', {resourceUrl: url});
      });

      this._storage = {};
    } else {
      delete this._storage[resourceUrl];
      this.emitter.emit('invalidate', {resourceUrl});
    }
  }

  /**
   * Resolve cache and return indexed data
   * @param {String} resourceUrl - Resource url
   * @param {String} cacheToken - Cache token
   * @see {@link PaginatedResourceListing#cacheToken}
   * @returns {Array} - Indexed relevant data
   * @todo add page numbers or range as optional parameter
   */
  resolve(resourceUrl, cacheToken = '') {
    cacheToken = cacheToken.toLowerCase();

    const data = this.collectPages(resourceUrl, cacheToken);
    const out = [];

    let lastStart;
    let lastEnd;
    let lastPage;

    for (const row of data) {
      const page = row.page;

      if (page.rows === 0) {
        // We can't do anything if we don't have any data
        continue;
      }

      const ids = page.data.map(row => row.id);
      const startId = Math.min(...ids);
      const endId = Math.max(...ids);

      const badKeys = [];

      if (page.page - 1 === lastPage || page.page === lastPage) {
        badKeys.push(...this._diffRange(lastEnd, startId));
      }

      if (page.page + 1 === lastPage || page.page === lastPage) {
        badKeys.push(...this._diffRange(endId, lastStart));
      }

      lastStart = startId;
      lastEnd = endId;

      for (const row of page.data) {
        out[row.id] = row;
      }

      if (!row.diff) {
        // Grab list of applicable ids and delete offending
        // keys that no longer exist in the newer data set
        Object
          .keys(out)
          .map(Number)
          .filter(key => startId <= key && key <= endId)
          .filter(key => !ids.includes(key))
          .forEach(key => badKeys.push(key));
      }

      badKeys.forEach(key => delete out[key]);
    }

    if (this.dereference) {
      return out.map(x => x.clone());
    }

    return out;
  }

  /**
   * Update records in the cache manually lazily. Any matching instance found will be updated.
   * @param {ResourceBase|Array<ResourceBase>} rows - Data to be updated
   * @returns {void} - nothing
   */
  update(rows) {
    if (!(rows instanceof Array)) {
      this.update([rows]);
      return;
    }

    // Split up data into types
    const data = {};
    const ids = {};

    for (const row of rows) {
      const key = row.constructor.name;

      (data[key] || (data[key] = [])).push(row);
      (ids[key] || (ids[key] = [])).push(row.id);
    }

    const models = Object.keys(data);

    for (const resourceUrl of Object.keys(this._storage)) {
      let invalidate = false;

      for (const token of Object.keys(this._storage[resourceUrl])) {
        const entries = this._storage[resourceUrl][token];

        for (const entry of entries) {
          const page = entry.page;

          if (page.data.length === 0) {
            continue;
          }

          const key = page.data[0].constructor.name;

          if (!models.includes(key)) {
            break;
          }

          for (const row of page.data) {
            if (!ids[key].includes(row.id)) {
              continue;
            }

            const index = ids[key].findIndex(x => x === row.id);
            const value = data[key][index];

            value.sanitize();

            value.fieldNames.forEach(x => {
              row[x] = value[x];
            });

            invalidate = true;
          }
        }
      }

      if (invalidate) {
        this.emitter.emit('invalidate', {resourceUrl: resourceUrl});
      }
    }
  }

  /**
   * Used for key elimination. Calculates the keys between two indexes.
   * @param {Number} start - Start index
   * @param {Number} end - End index
   * @returns {Array} - keys
   * @private
   * @example
   * cache._diffRange(1, 5) === [2, 3, 4]
   */
  _diffRange(start, end) {
    if (start > end) {
      const _x = end;

      end = start;
      start = _x;
    }

    if (start === end || start - end === 1) {
      return [];
    }

    const out = [];

    for (let i = start + 1; i < end; i++) {
      out.push(i);
    }

    return out;
  }

  /**
   * Get a usable timestamp
   * @returns {number} - timestamp
   * @private
   */
  get _timestamp() {
    return Math.floor(Date.now() / 1000);
  }
}
<commit_msg>Fix cache resolver<commit_after>/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import mitt from 'mitt';
import Unobservable from './utils/Unobservable';
import Uuid from './utils/uuid';

/**
 * Used for caching resources. Requires the resource to have an unique id field
 * @see {@link PaginatedResourceWrapper}
 * @todo Add periodic data refreshing while idle, most likely implemented in cache
 * @todo complete rewrite
 */
export default class ResourceCache extends Unobservable {
  constructor(api, cacheTime = api.defaults.cacheSeconds, dereference = api.defaults.dereferenceCache) {
    super();

    this._api = api;
    this.cacheTime = cacheTime;
    this.dereference = dereference;
    this.emitter = mitt();

    this._storage = {};
  }

  /**
   * Push a page into the cache
   * @param {PaginatedResourceListing} page - Data to be cached
   * @param {boolean} [diff=false] - Differential result used for updating the cache. Cache entry won't be used to for key dropping.
   * @returns {void}
   * @todo diff documentation
   */
  push(page, diff = false) {
    if (page.rows === 0) {
      return; // Don't insert empty pages
    }

    delete page['__ob__']; // Remove VueJs observer

    // Test if this is data we can actually work with by testing if there are any non-numeric ids (undefined etc)
    const invalidData = page.data.map(row => row.id).filter(x => typeof x !== 'number').length > 0;

    if (invalidData) {
      throw new TypeError('Missing or invalid row.id for page.data. Data rows must to contain a numeric "id" field.');
    }

    const validThrough = this._timestamp + this.cacheTime;
    const cacheId = Uuid.uuid4();
    const data = {
      page, validThrough, diff,
      id: cacheId,
      timeout: setTimeout(
        () => this._deleteCacheIds(cacheId),
        this.cacheTime * 1000,
      ),
    };

    const storage = this._storage[page.route] || (this._storage[page.route] = {});

    (storage[page.cacheToken] || (storage[page.cacheToken] = [])).push(data);

    this.emitter.emit('push', {page, validThrough, resourceUrl: page.route});
    this.emitter.emit('invalidate', {resourceUrl: page.route});
  }

  /**
   * Delete from cache using cacheId
   * @param {String|Array<String>} ids - cache ids
   * @returns {void}
   */
  _deleteCacheIds(ids) {
    if (!(ids instanceof Array)) {
      this._deleteCacheIds([ids]);
      return;
    }

    let found = 0;

    for (const resourceUrl of Object.keys(this._storage)) {
      for (const token of Object.keys(this._storage[resourceUrl])) {
        const entries = this._storage[resourceUrl][token];

        for (let i = 0; i < entries.length; i++) {
          if (ids.includes(entries[i].id)) {
            entries.splice(i, 1);
            i--;
            found++;

            if (found === ids.length) {
              return;
            }
          }
        }
      }
    }
  }

  /**
   * Revalidate all data and delete stale data
   * @param {String} resourceUrl - Resource url
   * @returns {void}
   */
  revalidate(resourceUrl = null) {
    if (!resourceUrl) {
      Object.keys(this._storage).map(this.revalidate);
    } else if (this._storage[resourceUrl]) {
      const storage = this._storage[resourceUrl];

      // Remove old data from the cache and stop old timeouts
      Object.keys(storage).forEach(key => {
        storage[key]
          .filter(row => row.validThrough < this._timestamp)
          .forEach(row => clearTimeout(row.timeout));

        storage[key] = storage[key].filter(row => row.validThrough >= this._timestamp);
      });

      const junk = Object.keys(storage).filter(key => storage[key].length === 0);

      // Delete empty
      junk.forEach(key => delete storage[key]);
      if (Object.keys(storage).length === 0) {
        delete this._storage[resourceUrl];
      }

      if (junk.length > 0) {
        this.emitter.emit('invalidate', {resourceUrl});
      }
    }
  }

  /**
   * Collect relevant cached pages
   * @param {String} resourceUrl - resource url
   * @param {String} cacheToken - Cache token
   * @see {@link PaginatedResourceListing#cacheToken}
   * @returns {Array<PaginatedResourceListing>} - Relevant cached pages
   */
  collectPages(resourceUrl, cacheToken = '') {
    cacheToken = cacheToken.toLowerCase();

    // Storage array or []
    const storage = (this._storage[resourceUrl] || {})[cacheToken] || [];

    // Sort by validThrough and extract pages
    // SORT BY page, validThrough ASCENDING
    return storage.sort((a, b) => {
      if (a.page === b.page) {
        return a.validThrough - b.validThrough;
      }

      return a.page - b.page;
    });
  }

  /**
   * Clears the cache
   * @param {String} resourceUrl - Resource url
   * @returns {void}
   */
  clear(resourceUrl = '') {
    if (!resourceUrl) {
      Object.keys(this._storage).forEach(url => {
        this.emitter.emit('invalidate', {resourceUrl: url});
      });

      this._storage = {};
    } else {
      delete this._storage[resourceUrl];
      this.emitter.emit('invalidate', {resourceUrl});
    }
  }

  /**
   * Resolve cache and return indexed data
   * @param {String} resourceUrl - Resource url
   * @param {String} cacheToken - Cache token
   * @see {@link PaginatedResourceListing#cacheToken}
   * @returns {Array} - Indexed relevant data
   * @todo add page numbers or range as optional parameter
   */
  resolve(resourceUrl, cacheToken = '') {
    cacheToken = cacheToken.toLowerCase();

    // row.diff should indicate that the records should just be
    // applied to the object and not used to diff out old records
    // List ordered from old to new
    const data = this.collectPages(resourceUrl, cacheToken);
    const out = [];

    let lastPage;
    let startIndex = 0;

    for (const row of data) {
      const page = row.page;

      if (page.data.length === 0) {
        continue;
      }

      // @todo compare `updatedAt` instead of assuming later pages are more recent
      if (row.diff) {
        continue; // @todo
      } else if (lastPage === page.page) {
        let ii;

        for (let i = 0; i < page.data.length; i++) {
          ii = i + startIndex;

          if (typeof out[ii] === 'undefined') {
            out.push(page.data[i]);
          } else if (page.data[i].id !== out[ii].id) {
            out[ii] = page.data[i];

            // lookbehind
            for (let j = 0; j < startIndex; j++) {
              if (out[j].id === out[ii].id) {
                out.splice(j, 1);

                startIndex--;
                i--;
                ii--;
                j--;
              }
            }
          }
        }

        // Remove trailing data
        if (typeof ii !== 'undefined') {
          out.splice(ii, out.length);
        }
      } else {
        startIndex = out.length;

        page.data.map(x => out.push(x));
      }

      lastPage = row.page;
    }

    if (this.dereference) {
      return out.map(x => x.clone());
    }

    return out;
  }

  /**
   * Update records in the cache manually lazily. Any matching instance found will be updated.
   * @param {ResourceBase|Array<ResourceBase>} rows - Data to be updated
   * @returns {void} - nothing
   */
  update(rows) {
    if (!(rows instanceof Array)) {
      this.update([rows]);
      return;
    }

    // Split up data into types
    const data = {};
    const ids = {};

    for (const row of rows) {
      const key = row.constructor.name;

      (data[key] || (data[key] = [])).push(row);
      (ids[key] || (ids[key] = [])).push(row.id);
    }

    const models = Object.keys(data);

    for (const resourceUrl of Object.keys(this._storage)) {
      let invalidate = false;

      for (const token of Object.keys(this._storage[resourceUrl])) {
        const entries = this._storage[resourceUrl][token];

        for (const entry of entries) {
          const page = entry.page;

          if (page.data.length === 0) {
            continue;
          }

          const key = page.data[0].constructor.name;

          if (!models.includes(key)) {
            break;
          }

          for (const row of page.data) {
            if (!ids[key].includes(row.id)) {
              continue;
            }

            const index = ids[key].findIndex(x => x === row.id);
            const value = data[key][index];

            value.sanitize();

            value.fieldNames.forEach(x => {
              row[x] = value[x];
            });

            invalidate = true;
          }
        }
      }

      if (invalidate) {
        this.emitter.emit('invalidate', {resourceUrl: resourceUrl});
      }
    }
  }

  /**
   * Used for key elimination. Calculates the keys between two indexes.
   * @param {Number} start - Start index
   * @param {Number} end - End index
   * @returns {Array} - keys
   * @private
   * @example
   * cache._diffRange(1, 5) === [2, 3, 4]
   */
  _diffRange(start, end) {
    if (start > end) {
      const _x = end;

      end = start;
      start = _x;
    }

    if (start === end || start - end === 1) {
      return [];
    }

    const out = [];

    for (let i = start + 1; i < end; i++) {
      out.push(i);
    }

    return out;
  }

  /**
   * Get a usable timestamp
   * @returns {number} - timestamp
   * @private
   */
  get _timestamp() {
    return Math.floor(Date.now() / 1000);
  }
}
<|endoftext|>


<commit_before>$(document).ready(function(){
	var url = window.location;
	$('ul.nav a').filter(function() {
    	return this.href == url;
	}).parent().addClass('active');

	// currentUrl = url.href;
	// currentUrl = currentUrl.split('/');
	// len = currentUrl.length;
	// currentUrl = currentUrl[len-1];
	// $("#left").on('click', function(){
	// 	curr = parseInt(currentUrl);
	// 	if(currentUrl == "01"){
	// 		window.location.href = "http://localhost:5000/video/14"
	// 	}
	// 	else if(currentUrl == "15"){
	// 		window.location.href = "http://localhost:5000/video/13"
	// 	}
	// 	else if(currentUrl = "14"){
	// 		window.location.href = "http://localhost:5000/video/15"
	// 	}
	// 	else{
	// 		curr -= 1;
	// 		if(curr < 10){
	// 			curr = "0" + curr.toString();
	// 		}
	// 		else{
	// 			curr = curr.toString();
	// 		}
	// 		window.location.href = "http://localhost:5000/video/" + curr;
	// 	}
	// });
	// $("#right").on('click', function(){
	// 	curr = parseInt(currentUrl);
	// 	if(currentUrl == "14"){
	// 		window.location.href = "http://localhost:5000/video/01"
	// 	}
	// 	else if(currentUrl == "13"){
	// 		window.location.href = "http://localhost:5000/video/15"
	// 	}
	// 	else if(currentUrl = "15"){
	// 		window.location.href = "http://localhost:5000/video/14"
	// 	}
	// 	else{
	// 		curr += 1;
	// 		if(curr < 10){
	// 			curr = "0" + curr.toString();
	// 		}
	// 		else{
	// 			curr = curr.toString();
	// 		}
	// 		window.location.href = "http://localhost:5000/video/" + curr;
	// 	}
	// });
});<commit_msg>Left Right button fix<commit_after>$(document).ready(function(){
	var url = window.location;
	$('ul.nav a').filter(function() {
    	return this.href == url;
	}).parent().addClass('active');

	currentUrl = url.href;
	currentUrl = currentUrl.split('/');
	len = currentUrl.length;
	currentUrl = currentUrl[len-1];
	$("#left").on('click', function(){
		curr = parseInt(currentUrl);
		if(currentUrl == "01"){
			window.location.href = "http://localhost:5000/video/14"
		}
		else if(currentUrl == "15"){
			window.location.href = "http://localhost:5000/video/13"
		}
		else if(currentUrl == "14"){
			window.location.href = "http://localhost:5000/video/15"
		}
		else{
			curr -= 1;
			if(curr < 10){
				curr = "0" + curr.toString();
			}
			else{
				curr = curr.toString();
			}
			window.location.href = "http://localhost:5000/video/" + curr;
		}
	});
	$("#right").on('click', function(){
		curr = parseInt(currentUrl);
		if(currentUrl == "14"){
			window.location.href = "http://localhost:5000/video/01"
		}
		else if(currentUrl == "13"){
			window.location.href = "http://localhost:5000/video/15"
		}
		else if(currentUrl == "15"){
			window.location.href = "http://localhost:5000/video/14"
		}
		else{
			curr += 1;
			if(curr < 10){
				curr = "0" + curr.toString();
			}
			else{
				curr = curr.toString();
			}
			window.location.href = "http://localhost:5000/video/" + curr;
		}
	});
});<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import Options from './Options';
import Logout from '../Logout/index';
import jwt from 'jsonwebtoken';

class Sandbox extends Component {
  constructor (props) {
    super(props);
  }

  componentDidMount() {
    var token = localStorage.jwtToken;
    this.props.dispatch({type:"VALIDATE_AUTH", user:jwt.decode(token)});
  }

  render () {
    return(

Hello, {this.props.user}

<Link to ="/Wiki">LEARN/INFORMATION</Link>

<Link to ="/settings">Profile settings</Link>

        <Logout />
        <Options />

    )
  }
}

export default connect((store) => {
  return {
    user: store.auth.authentication.user.username
  }
})(Sandbox);<commit_msg>start on user section of dashboard design<commit_after>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import Options from './Options';
import Logout from '../Logout/index';
import jwt from 'jsonwebtoken';

class Sandbox extends Component {
  constructor (props) {
    super(props);
  }

  componentDidMount() {
    var token = localStorage.jwtToken;
    this.props.dispatch({type:"VALIDATE_AUTH", user:jwt.decode(token)});
  }

  render () {
    return(

Hello, {this.props.user}

<Link to ="/settings">Profile settings</Link>

            <Logout />

<Link to ="/Wiki">LEARN/INFORMATION</Link>

        <Options />

    )
  }
}

export default connect((store) => {
  return {
    user: store.auth.authentication.user.username
  }
})(Sandbox);<|endoftext|>