<commit_before>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

const assert = require('assert');
const URL = require('url').URL;

const experimentName = 'helloworld-experiment-' + Date.now();
const experimentDescription = 'hello world experiment description';
const pipelineName = 'helloworld-pipeline-' + Date.now();
const runName = 'helloworld-' + Date.now();
const runDescription = 'test run description ' + runName;
const waitTimeout = 5000;
const outputParameterValue = 'Hello world in test'

function getValueFromDetailsTable(key) {
  // Find the span that shows the key, get its parent div (the row), then
  // get that row's inner text, and remove the key
  const row = $(`span=${key}`).$('..');
  return row.getText().substr(`${key}\n`.length);
}

describe('deploy helloworld sample run', () => {

  before(() => {
    browser.url('/');
  });

  it('opens the pipeline upload dialog', () => {
    $('#uploadBtn').click();
    browser.waitForVisible('#uploadDialog', waitTimeout);
  });

  it('uploads the sample pipeline', () => {
    browser.chooseFile('#uploadDialog input[type="file"]', './helloworld.yaml');
    const input = $('#uploadDialog #uploadFileName');
    input.clearElement();
    input.setValue(pipelineName);
    $('#confirmUploadBtn').click();
    browser.waitForVisible('#uploadDialog', waitTimeout, true);
  });

  it('opens pipeline details', () => {
    $('.tableRow a').waitForVisible(waitTimeout);
    browser.execute('document.querySelector(".tableRow a").click()');
  });

  it('shows a 4-node static graph', () => {
    const nodeSelector = '.graphNode';
    $(nodeSelector).waitForVisible();
    const nodes = $$(nodeSelector).length;
    assert(nodes === 4, 'should have a 4-node graph, instead has: ' + nodes);
  });

  it('creates a new experiment out of this pipeline', () => {
    $('#startNewExperimentBtn').click();
    browser.waitUntil(() => {
      return new URL(browser.getUrl()).hash.startsWith('#/experiments/new');
    }, waitTimeout);

    $('#experimentName').setValue(experimentName);
    $('#experimentDescription').setValue(experimentDescription);

    $('#createExperimentBtn').click();
  });

  it('creates a new run in the experiment', () => {
    $('#choosePipelineBtn').waitForVisible();
    $('#choosePipelineBtn').click();

    $('.tableRow').waitForVisible();
    $('.tableRow').click();

    $('#usePipelineBtn').click();

    $('#pipelineSelectorDialog').waitForVisible(waitTimeout, true);

    browser.keys('Tab');
    browser.keys(runName);

    browser.keys('Tab');
    browser.keys(runDescription);

    browser.keys('Tab');
    browser.keys(outputParameterValue);

    // Deploy
    $('#createNewRunBtn').click();
  });

  it('redirects back to experiment page', () => {
    browser.waitUntil(() => {
      return new URL(browser.getUrl()).hash.startsWith('#/experiments/details/');
    }, waitTimeout);
  });

  it('finds the new run in the list of runs, navigates to it', () => {
    $('.tableRow').waitForVisible(3 * waitTimeout);
    assert.equal($$('.tableRow').length, 1, 'should only show one run');

    // Navigate to details of the deployed run by clicking its anchor element
    $('.tableRow a').waitForVisible(waitTimeout);
    browser.execute('document.querySelector(".tableRow a").click()');
  });

  it('switches to config tab', () => {
    $('button=Config').waitForVisible(waitTimeout);
    $('button=Config').click();
  });

  it('waits for run to finish', () => {
    let status = getValueFromDetailsTable('Status');

    let attempts = 0;
    const maxAttempts = 60;

    // Wait for a reasonable amount of time until the run is done
    while (attempts < maxAttempts && status.trim() !== 'Succeeded') {
      browser.pause(1000);
      $('#refreshBtn').click();
      status = getValueFromDetailsTable('Status');
      attempts++;
    }

    assert(attempts < maxAttempts, `waited for ${maxAttempts} seconds but run did not succeed. ` +
      'Current status is: ' + status);
  });

  it('displays run created at date correctly', () => {
    const date = getValueFromDetailsTable('Created at');
    assert(Date.now() - new Date(date) < 10 * 60 * 1000,
      'run created date should be within the last 10 minutes');
  });

  it('displays run inputs correctly', () => {
    const paramValue = getValueFromDetailsTable('message');
    assert.equal(paramValue, outputParameterValue, 'run message is not shown correctly');
  });

  it('switches back to graph tab', () => {
    $('button=Graph').click();
  });

  it('has a 4-node graph', () => {
    const nodeSelector = '.graphNode';
    const nodes = $$(nodeSelector).length;
    assert(nodes === 4, 'should have a 4-node graph, instead has: ' + nodes);
  });

  it('opens the side panel when graph node is clicked', () => {
    $('.graphNode').click();
    $('button=Logs').waitForVisible();
  });

  it('shows logs from node', () => {
    $('button=Logs').click();
    $('#logViewer').waitForVisible();
    const logs = $('#logViewer').getText();
    assert(logs.indexOf(outputParameterValue + ' from node: ') > -1,
      'logs do not look right: ' + logs);
  });
  //TODO: enable this after we change the pipeline to a unique name such that deleting this
  // pipeline will not jeopardize the concurrent basic e2e tests.
  // it('deletes the uploaded pipeline', () => {
  //   $('#pipelinesBtn').click();
  //
  //   browser.waitForVisible('.tableRow', waitTimeout);
  //   $('.tableRow').click();
  //   $('#deleteBtn').click();
  //   $('.dialogButton').click();
  //   $('.dialog').waitForVisible(waitTimeout, true);
  // });
});
<commit_msg>Wait until logs appear right (#459)<commit_after>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

const assert = require('assert');
const URL = require('url').URL;

const experimentName = 'helloworld-experiment-' + Date.now();
const experimentDescription = 'hello world experiment description';
const pipelineName = 'helloworld-pipeline-' + Date.now();
const runName = 'helloworld-' + Date.now();
const runDescription = 'test run description ' + runName;
const waitTimeout = 5000;
const outputParameterValue = 'Hello world in test'

function getValueFromDetailsTable(key) {
  // Find the span that shows the key, get its parent div (the row), then
  // get that row's inner text, and remove the key
  const row = $(`span=${key}`).$('..');
  return row.getText().substr(`${key}\n`.length);
}

describe('deploy helloworld sample run', () => {

  before(() => {
    browser.url('/');
  });

  it('opens the pipeline upload dialog', () => {
    $('#uploadBtn').click();
    browser.waitForVisible('#uploadDialog', waitTimeout);
  });

  it('uploads the sample pipeline', () => {
    browser.chooseFile('#uploadDialog input[type="file"]', './helloworld.yaml');
    const input = $('#uploadDialog #uploadFileName');
    input.clearElement();
    input.setValue(pipelineName);
    $('#confirmUploadBtn').click();
    browser.waitForVisible('#uploadDialog', waitTimeout, true);
  });

  it('opens pipeline details', () => {
    $('.tableRow a').waitForVisible(waitTimeout);
    browser.execute('document.querySelector(".tableRow a").click()');
  });

  it('shows a 4-node static graph', () => {
    const nodeSelector = '.graphNode';
    $(nodeSelector).waitForVisible();
    const nodes = $$(nodeSelector).length;
    assert(nodes === 4, 'should have a 4-node graph, instead has: ' + nodes);
  });

  it('creates a new experiment out of this pipeline', () => {
    $('#startNewExperimentBtn').click();
    browser.waitUntil(() => {
      return new URL(browser.getUrl()).hash.startsWith('#/experiments/new');
    }, waitTimeout);

    $('#experimentName').setValue(experimentName);
    $('#experimentDescription').setValue(experimentDescription);

    $('#createExperimentBtn').click();
  });

  it('creates a new run in the experiment', () => {
    $('#choosePipelineBtn').waitForVisible();
    $('#choosePipelineBtn').click();

    $('.tableRow').waitForVisible();
    $('.tableRow').click();

    $('#usePipelineBtn').click();

    $('#pipelineSelectorDialog').waitForVisible(waitTimeout, true);

    browser.keys('Tab');
    browser.keys(runName);

    browser.keys('Tab');
    browser.keys(runDescription);

    browser.keys('Tab');
    browser.keys(outputParameterValue);

    // Deploy
    $('#createNewRunBtn').click();
  });

  it('redirects back to experiment page', () => {
    browser.waitUntil(() => {
      return new URL(browser.getUrl()).hash.startsWith('#/experiments/details/');
    }, waitTimeout);
  });

  it('finds the new run in the list of runs, navigates to it', () => {
    $('.tableRow').waitForVisible(3 * waitTimeout);
    assert.equal($$('.tableRow').length, 1, 'should only show one run');

    // Navigate to details of the deployed run by clicking its anchor element
    $('.tableRow a').waitForVisible(waitTimeout);
    browser.execute('document.querySelector(".tableRow a").click()');
  });

  it('switches to config tab', () => {
    $('button=Config').waitForVisible(waitTimeout);
    $('button=Config').click();
  });

  it('waits for run to finish', () => {
    let status = getValueFromDetailsTable('Status');

    let attempts = 0;
    const maxAttempts = 60;

    // Wait for a reasonable amount of time until the run is done
    while (attempts < maxAttempts && status.trim() !== 'Succeeded') {
      browser.pause(1000);
      $('#refreshBtn').click();
      status = getValueFromDetailsTable('Status');
      attempts++;
    }

    assert(attempts < maxAttempts, `waited for ${maxAttempts} seconds but run did not succeed. ` +
      'Current status is: ' + status);
  });

  it('displays run created at date correctly', () => {
    const date = getValueFromDetailsTable('Created at');
    assert(Date.now() - new Date(date) < 10 * 60 * 1000,
      'run created date should be within the last 10 minutes');
  });

  it('displays run inputs correctly', () => {
    const paramValue = getValueFromDetailsTable('message');
    assert.equal(paramValue, outputParameterValue, 'run message is not shown correctly');
  });

  it('switches back to graph tab', () => {
    $('button=Graph').click();
  });

  it('has a 4-node graph', () => {
    const nodeSelector = '.graphNode';
    const nodes = $$(nodeSelector).length;
    assert(nodes === 4, 'should have a 4-node graph, instead has: ' + nodes);
  });

  it('opens the side panel when graph node is clicked', () => {
    $('.graphNode').click();
    $('button=Logs').waitForVisible();
  });

  it('shows logs from node', () => {
    $('button=Logs').click();
    $('#logViewer').waitForVisible();
    browser.waitUntil(() => {
      const logs = $('#logViewer').getText();
      return logs.indexOf(outputParameterValue + ' from node: ') > -1;
    }, waitTimeout);
  });
  //TODO: enable this after we change the pipeline to a unique name such that deleting this
  // pipeline will not jeopardize the concurrent basic e2e tests.
  // it('deletes the uploaded pipeline', () => {
  //   $('#pipelinesBtn').click();
  //
  //   browser.waitForVisible('.tableRow', waitTimeout);
  //   $('.tableRow').click();
  //   $('#deleteBtn').click();
  //   $('.dialogButton').click();
  //   $('.dialog').waitForVisible(waitTimeout, true);
  // });
});
<|endoftext|>


<commit_before>var Base64 = require('base-64')

function exportJSON(lessons, filename='export.json') {
  var json = JSON.stringify(lessons, null, 2)
  var json64 = Base64.encode(json)
  var a = document.createElement('a')
  if ('download' in a) {
    a.href = `data:text/json;base64,${json64}`
    a.download = filename
    var event = document.createEvent('MouseEvents')
    event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0,
                         false, false, false, false, 0, null)
    a.dispatchEvent(event)
  }
  else if (typeof navigator.msSaveBlob == 'function') {
    navigator.msSaveBlob(new window.Blob([json], {
      type: 'text/json;charset=utf-8;'
    }), filename)
  }
  else {
    window.location.href = `data:application/octet-stream;base64,${json64}`
  }
}

module.exports = exportJSON
<commit_msg>Encoding fix for exporting<commit_after>var Base64 = require('base-64')

function exportJSON(lessons, filename='export.json') {
  var json = JSON.stringify(lessons, null, 2)
  var json64 = Base64.encode(unescape(encodeURIComponent(json)))
  var a = document.createElement('a')
  if ('download' in a) {
    a.href = `data:text/json;base64,${json64}`
    a.download = filename
    var event = document.createEvent('MouseEvents')
    event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0,
                         false, false, false, false, 0, null)
    a.dispatchEvent(event)
  }
  else if (typeof navigator.msSaveBlob == 'function') {
    navigator.msSaveBlob(new window.Blob([json], {
      type: 'text/json;charset=utf-8;'
    }), filename)
  }
  else {
    window.location.href = `data:application/octet-stream;base64,${json64}`
  }
}

module.exports = exportJSON
<|endoftext|>


<commit_before>var Promise = require('promise');
var extractJSON = require('./extract-json');
var jsonFormater = require('./jsl-format');

function contentExtractor(pre) {
  return new Promise(function(resolve, reject) {
    try {
      var rawJsonText = pre.textContent;
      var jsonExtracted = extractJSON(rawJsonText);

      // validate json
      JSON.parse(jsonExtracted);

      var jsonText = jsonFormater(rawJsonText);
      resolve({jsonText: jsonText, jsonExtracted: jsonExtracted})

    } catch(e) {
      reject(e);
    }
  });
}

module.exports = contentExtractor;
<commit_msg>decoding json before highlight (closes #34, closes #24 and closes #4)<commit_after>var Promise = require('promise');
var extractJSON = require('./extract-json');
var jsonFormater = require('./jsl-format');

function contentExtractor(pre) {
  return new Promise(function(resolve, reject) {
    try {
      var rawJsonText = pre.textContent;
      var jsonExtracted = extractJSON(rawJsonText);

      // validate and decode json
      var decodedJson = JSON.stringify(JSON.parse(jsonExtracted));
      var jsonText = rawJsonText.replace(jsonExtracted, jsonFormater(decodedJson));

      resolve({jsonText: jsonText, jsonExtracted: decodedJson});

    } catch(e) {
      reject(e);
    }
  });
}

module.exports = contentExtractor;
<|endoftext|>


<commit_before>var debug = require('debug'),
    _ = require('underscore'),
    fs = require('fs'),
    minimist = require('minimist'),
    Q = require('q'),
    path = require('path'),
    glob = require('glob');

var log = debug('rename/logs'),
    info = debug('rename/info'),
    warn = debug('rename/warnnings'),
    error = debug('rename/errors');

var options = {
    string: [ 'm', 'f', 'p', 's' ],
    boolean: [ 'v' ],
    default: {
        f: '',
        m: '{{*}}',
        p: '',
        s: '_'
    },
    alias: {
        v: 'verbose',
        m: 'map',
        f: 'filter',
        p: 'path',
        s: 'separator'
    }
};
var argv = minimist(process.argv.slice(2), options);

var list = glob.sync(path.join(argv.path, '*.req'));
info('Proccessing ' + list.length + ' files');

var promiseList = _.chain(list)
    .map(function(item) {
        return Q.Promise(function(resolve, reject) {
            log('checking file', item);
            fs.readFile(path.join(__dirname, item), { encoding: 'utf8' }, function(err, text) {
                if (err) {
                    reject(err);
                    return;
                }
                var data = JSON.parse(text);
                var filter = argv.f.split(',');
                var test = _.all(filter, function(filter) {
                    var tokens = filter.split('=');
                    log(filter, tokens);
                    var vars = tokens[0].split('|');
                    return _.all(vars, function(key) {
                        if (data.hasOwnProperty(key)) {
                            return data[key].match(tokens[1]);
                        }
                        return true;
                    });
                });
                if (test) {
                    var first = true;
                    var line = argv.m.replace(/{{([\w\d|]+)}}/g, function(match, variable) {
                        var tokens = variable.split('|');
                        var sep = first ? '' : argv.s;
                        first = false;
                        log(match, variable, tokens);
                        for (var i = 0; i < tokens.length; ++i) {
                            if (data.hasOwnProperty(tokens[i])) {
                                var param = data[tokens[i]];
                                delete data[tokens[i]];
                                log(data);
                                return sep + param;
                            }
                        }
                        return '';
                    }).replace('{{*}}', function() {
                        var sep = first ? '' : argv.s;
                        return sep + Object.keys(data).map(function(key) { return data[key]; }).join(argv.s);
                    });
                    log('testing', data, line, filter, test);
                    resolve({ old: item, 'new': path.join(path.dirname(item), line) });
                } else {
                    log('testing', data, filter, test);
                    reject('filter out ' + item);
                }
            });
        });
    })
    .value();

Q.allSettled(promiseList)
    .then(function(list) {
        _.chain(list)
            .filter(function(item) { return item.state === 'fulfilled' && item.value; })
            .map(function(item) { return item.value; })
            .each(function(item) {
                var files = {};
                _.each(item, function(name, key) {
                    files[key] = path.join(__dirname, name);
                });
                if (files.old !== (files.new + '.req')) {
                    _.each(['req', 'res', 'stats'], function(ext) {
                        fs.rename(files.old.replace(/req$/, ext), files.new + '.' + ext, function(err) {
                            if (err) {
                                error('Error renaming', item.old);
                                error(err);
                                return;
                            }
                            info('file renamed', files);
                        });
                    });
                }
            });

        //var err = _.chain(list)
        //    .filter(function(item) { return item.state === 'rejected'; })
        //    .map(function(item) { return item.reason; })
        //    .value();
        //
        //console.log(err);
    });
<commit_msg>Refactor log to be turn on by flags Refactor use of '|' to ',' to work in linux and mac systems<commit_after>var debug = require('debug'),
    _ = require('underscore'),
    fs = require('fs'),
    minimist = require('minimist'),
    Q = require('q'),
    path = require('path'),
    glob = require('glob');

var options = {
    string: [ 'm', 'f', 'p', 's' ],
    boolean: [ 'v', 'V', 'P', 'l' ],
    default: {
        f: null,
        m: '{{*}}',
        p: '',
        s: '_',
        v: false,
        V: false,
        l: false
    },
    alias: {
        v: 'verbose',
        m: 'map',
        f: 'filter',
        p: 'path',
        s: 'separator'
    }
};
var argv = minimist(process.argv.slice(2), options);

debug.enable('rename/errors');
if (argv.v || argv.P || argv.V) {
    debug.enable('rename/info');
    debug.enable('rename/warnings');
    if (argv.P) {
        debug.enable('rename/promise');
    }
    if (argv.V) {
        debug.enable('rename/logs');
        debug.enable('rename/promise');
    }
}

var log = debug('rename/logs'),
    plog = debug('rename/promise'),
    info = debug('rename/info'),
    warn = debug('rename/warnnings'),
    error = debug('rename/errors');

var list = glob.sync(path.join(argv.path, '*.req'));
info('Proccessing ' + list.length + ' files');

var promiseList = _.chain(list)
    .map(function(item) {
        return Q.Promise(function(resolve, reject) {
            log('checking file', item);
            fs.readFile(path.join(__dirname, item), { encoding: 'utf8' }, function(err, text) {
                if (err) {
                    plog('reject file ' + item, err);
                    reject(err);
                    return;
                }
                var data = JSON.parse(text);
                var filter = [];
                if (argv.f) {
                    argv.f.split(',');
                }
                var test = _.all(filter, function(filter) {
                    var tokens = filter.split('=');
                    log(filter, tokens);
                    var vars = tokens[0].split('|');
                    return _.any(vars, function(key) {
                        if (data.hasOwnProperty(key)) {
                            return data[key].match(tokens[1]);
                        }
                        return false;
                    });
                });
                if (test) {
                    var first = true;
                    var line = argv.m.replace(/{{([\w\d,]+)}}/g, function(match, variable) {
                        var tokens = variable.split(',');
                        var sep = first ? '' : argv.s;
                        first = false;
                        log('match', match, variable, tokens);
                        for (var i = 0; i < tokens.length; ++i) {
                            if (data.hasOwnProperty(tokens[i])) {
                                var param = data[tokens[i]];
                                delete data[tokens[i]];
                                log(data);
                                return sep + param;
                            }
                        }
                        return '';
                    }).replace('{{*}}', function() {
                        var sep = first ? '' : argv.s;
                        return sep + Object.keys(data).map(function(key) { return data[key]; }).join(argv.s);
                    });
                    log('testing', data, line, filter, test);
                    plog('resolve file ' + item, line);
                    resolve({ old: item, 'new': path.join(path.dirname(item), line) });
                } else {
                    log('testing', data, filter, test);
                    plog('reject file ' + item, 'Filtered out');
                    reject('filtered out ' + item);
                }
            });
        });
    })
    .value();

Q.allSettled(promiseList)
    .then(function(list) {
        info('Renaming ' + _.chain(list)
            .filter(function(item) { return item.state === 'fulfilled' && item.value; })
            .map(function(item) { return item.value; })
             .value().length + ' files');
        _.chain(list)
            .filter(function(item) { return item.state === 'fulfilled' && item.value; })
            .map(function(item) { return item.value; })
            .each(function(item) {
                var files = {};
                _.each(item, function(name, key) {
                    files[key] = path.join(__dirname, name);
                });
                if (files.old !== (files.new + '.req')) {
                    _.each(['req', 'res', 'stats'], function(ext) {
                        fs.rename(files.old.replace(/req$/, ext), files.new + '.' + ext, function(err) {
                            if (err) {
                                error('Error renaming', item.old);
                                error(err);
                                return;
                            }
                            info('file renamed', files);
                        });
                    });
                }
            });

        //var err = _.chain(list)
        //    .filter(function(item) { return item.state === 'rejected'; })
        //    .map(function(item) { return item.reason; })
        //    .value();
        //
        //console.log(err);
    });
<|endoftext|>


<commit_before>var forever = require('forever');

var child = new (forever.Monitor)('web.js', {
  max: 3,
  silent: false,
  options: []
});

//child.on('exit', this.callback);
child.start();

forever.startServer(child);
<commit_msg>forever<commit_after>var forever = require('forever');

var child = new (forever.Monitor)('index.js', {
  max: 3,
  silent: false,
  options: []
});

//child.on('exit', this.callback);
child.start();

forever.startServer(child);
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var configHelper = require('../config');
var log = require('../logger')('nodecg/lib/dashboard');
var bundles = require('../bundles');
var favicon = require('express-favicon');
var path = require('path');
var fs = require('fs');
var ncgUtils = require('../util');
var jade = require('jade');

var app = express();
var filteredConfig = configHelper.getFilteredConfig();
var publicPath = path.join(__dirname, 'public');
var cachedDashboard = null;

log.trace('Adding Express routes');
app.use('/dashboard', express.static(publicPath, {maxAge: 60*60*24*7*1000}));
app.use(favicon(publicPath + '/img/favicon.ico'));
app.set('views', publicPath);

app.get('/', function(req, res) {
    res.redirect('/dashboard/');
});

app.get('/dashboard', ncgUtils.authCheck, function(req, res) {
    if (filteredConfig.developer) {
        res.render(__dirname + '/src/dashboard.jade', {
            bundles: bundles.all(),
            ncgConfig: filteredConfig
        });
    } else {
        res.status(200).send(cachedDashboard);
    }
});

bundles.all().forEach(function(bundle) {
    // Setup panel paths
    bundle.dashboard.panels.forEach(function(panel) {
        if (panel.dialog) {
            app.get('/panel/'+bundle.name+'/'+panel.name,
                ncgUtils.authCheck,
                function(req, res) {
                    ncgUtils.injectScripts(panel.html, 'dialog', {createApiInstance: bundle}, function(html) {
                        res.send(html);
                    });
                });
        } else {
            app.get('/panel/'+bundle.name+'/'+panel.name,
                ncgUtils.authCheck,
                function(req, res) {
                    ncgUtils.injectScripts(panel.html, 'panel', {createApiInstance: bundle}, function(html) {
                        res.send(html);
                    });
                });
        }
    });

    /* The font-roboto Polymer component makes a request to Google's servers to load the font file.
     * This takes a very long time, sometimes up to a second per request.
     * Many Polymer components include this element, so nearly every panel is making this request.
     * To reduce load times, we check to see if the resource being requested is the font-roboto component.
     * If so, we return our own local instance of the Roboto webfont.
     */
    app.get('/panel/:bundleName/components/font-roboto/roboto.html', function(req, res) {
        res.send(
            '<link rel="stylesheet" href="/dashboard/font/Roboto/roboto.css">' +
            '<link rel="stylesheet" href="/dashboard/font/Roboto_Mono/roboto_mono.css">'
        );
    });

    // Setup components
    app.use('/panel/'+bundle.name+'/components', ncgUtils.authCheck,
        express.static(path.join(bundle.dir, 'bower_components'), {maxAge: 60*60*24*7*1000}));

    // Setup extra files
    app.use('/panel/'+bundle.name+'/', ncgUtils.authCheck,
        express.static(bundle.dashboard.dir, {maxAge: 60*60*24*7*1000}));
});

function cacheDashboard() {
    cachedDashboard = jade.renderFile(__dirname + '/src/dashboard.jade', {
        bundles: bundles.all(),
        ncgConfig: filteredConfig
    });
}

// When all bundles are first loaded, render and cache the dashboard
bundles.on('allLoaded', cacheDashboard);

// When a panel is re-parsed, re-render and cache the dashboard
bundles.on('bundleChanged', cacheDashboard);

module.exports = app;
<commit_msg>[dashboard] Remove unused 'fs' include<commit_after>'use strict';

var express = require('express');
var configHelper = require('../config');
var log = require('../logger')('nodecg/lib/dashboard');
var bundles = require('../bundles');
var favicon = require('express-favicon');
var path = require('path');
var ncgUtils = require('../util');
var jade = require('jade');

var app = express();
var filteredConfig = configHelper.getFilteredConfig();
var publicPath = path.join(__dirname, 'public');
var cachedDashboard = null;

log.trace('Adding Express routes');
app.use('/dashboard', express.static(publicPath, {maxAge: 60*60*24*7*1000}));
app.use(favicon(publicPath + '/img/favicon.ico'));
app.set('views', publicPath);

app.get('/', function(req, res) {
    res.redirect('/dashboard/');
});

app.get('/dashboard', ncgUtils.authCheck, function(req, res) {
    if (filteredConfig.developer) {
        res.render(__dirname + '/src/dashboard.jade', {
            bundles: bundles.all(),
            ncgConfig: filteredConfig
        });
    } else {
        res.status(200).send(cachedDashboard);
    }
});

bundles.all().forEach(function(bundle) {
    // Setup panel paths
    bundle.dashboard.panels.forEach(function(panel) {
        if (panel.dialog) {
            app.get('/panel/'+bundle.name+'/'+panel.name,
                ncgUtils.authCheck,
                function(req, res) {
                    ncgUtils.injectScripts(panel.html, 'dialog', {createApiInstance: bundle}, function(html) {
                        res.send(html);
                    });
                });
        } else {
            app.get('/panel/'+bundle.name+'/'+panel.name,
                ncgUtils.authCheck,
                function(req, res) {
                    ncgUtils.injectScripts(panel.html, 'panel', {createApiInstance: bundle}, function(html) {
                        res.send(html);
                    });
                });
        }
    });

    /* The font-roboto Polymer component makes a request to Google's servers to load the font file.
     * This takes a very long time, sometimes up to a second per request.
     * Many Polymer components include this element, so nearly every panel is making this request.
     * To reduce load times, we check to see if the resource being requested is the font-roboto component.
     * If so, we return our own local instance of the Roboto webfont.
     */
    app.get('/panel/:bundleName/components/font-roboto/roboto.html', function(req, res) {
        res.send(
            '<link rel="stylesheet" href="/dashboard/font/Roboto/roboto.css">' +
            '<link rel="stylesheet" href="/dashboard/font/Roboto_Mono/roboto_mono.css">'
        );
    });

    // Setup components
    app.use('/panel/'+bundle.name+'/components', ncgUtils.authCheck,
        express.static(path.join(bundle.dir, 'bower_components'), {maxAge: 60*60*24*7*1000}));

    // Setup extra files
    app.use('/panel/'+bundle.name+'/', ncgUtils.authCheck,
        express.static(bundle.dashboard.dir, {maxAge: 60*60*24*7*1000}));
});

function cacheDashboard() {
    cachedDashboard = jade.renderFile(__dirname + '/src/dashboard.jade', {
        bundles: bundles.all(),
        ncgConfig: filteredConfig
    });
}

// When all bundles are first loaded, render and cache the dashboard
bundles.on('allLoaded', cacheDashboard);

// When a panel is re-parsed, re-render and cache the dashboard
bundles.on('bundleChanged', cacheDashboard);

module.exports = app;
<|endoftext|>


<commit_before>function Fortune(files) {
    this.fortunes = [];
    this.append_files(files);
}

Fortune.prototype.append_files = function(files) {

    if(!files) {
        // Prevents from appending the current HTML document as a fortune...
        return;
    }

    files = files instanceof Array ? files : [files];

    for(var i in files) {
        this._append_file(files[i]);
    }
}

Fortune.prototype._append_file = function(file) {
    this.fortunes = [];
    var that = this;
    $.get(file, function(txt) {
        var lines = txt.split("\n");

        var index = that.fortunes.length;
        that.fortunes[index] = '';

        for(var i in lines) {
            if(lines[i] == '%') {
                // Remove the last \n
                that.fortunes[index] = that.fortunes[index].substr(0, that.fortunes[index].length - 1);
                index++;
                that.fortunes[index] = '';
                continue;
            }

            that.fortunes[index] += lines[i] + "\n";
        }
    }, 'text').fail(function() {
        console.error('Invalid fortune file');
    });
    return this;
};

Fortune.prototype.empty = function() {
    this.fortunes = [];
    return this;
};

Fortune.prototype.next = function() {
    return this.fortunes[Math.floor(Math.random() * this.fortunes.length)];
};
<commit_msg>Removes the last fortune entry if it's empty<commit_after>function Fortune(files) {
    this.fortunes = [];
    this.append_files(files);
}

Fortune.prototype.append_files = function(files) {

    if(!files) {
        // Prevents from appending the current HTML document as a fortune...
        return;
    }

    files = files instanceof Array ? files : [files];

    for(var i in files) {
        this._append_file(files[i]);
    }
}

Fortune.prototype._append_file = function(file) {
    this.fortunes = [];
    var that = this;
    $.get(file, function(txt) {
        var lines = txt.split("\n");

        var index = that.fortunes.length;
        that.fortunes[index] = '';

        for(var i in lines) {
            if(lines[i] == '%') {
                // Remove the last \n
                that.fortunes[index] = that.fortunes[index].substr(0, that.fortunes[index].length - 1);
                index++;
                that.fortunes[index] = '';
                continue;
            }

            that.fortunes[index] += lines[i] + "\n";
        }

        console.log(that.fortunes[index]);
        if(that.fortunes[index] == "\n") {
            // Removes the last entry if it's empty (if the file ended with a %)
            that.fortunes.pop();
        }

    }, 'text').fail(function() {
        console.error('Invalid fortune file');
    });
    return this;
};

Fortune.prototype.empty = function() {
    this.fortunes = [];
    return this;
};

Fortune.prototype.next = function() {
    return this.fortunes[Math.floor(Math.random() * this.fortunes.length)];
};
<|endoftext|>


<commit_before>/* Dice Physics
 * this is open source, please refer to the licences for details.
 *
 * Dice Physics is designed to replace Math.random() with the physics
 * of actual dicerolls.
 * Dice rolls are not purely random, they are complex Neutonian
 * near-chaotic systems.
 *
 * The calculations here should emulate real physics in three instances:
 * 1. The shaking of the die in the hand.
 * 2. The trajectory and spin of the die as it leaves the hand.
 * 3. The bounce as it hits the table and comes to a rest.
 *
 * It will need to accept as input:
 * 1. The initial facing of the die before it is thrown.
 * 2. The shake data from the accelorameter to determine the spin.
 * 3. The release data from the accelorameter to determine trajectory
 *    and more importantly, The vector of contact with the virtual table.
 *
 * It will return the die's upward face as the result of the roll.
 *
 * Settable constants such as height from,
 * and friction coeficient of the virtual table will also play roles.
 *
 * If done right, this may have aplications beyond playing games.
 */

//todo: research accelorometers. What output to they give?
//This little beast needs to be designed to eat that kind of output
//and poop out a dice roll.

//todo: research physics formulae,
//and how other games derrive their realistic-looking physics
<commit_msg>add more work on spec<commit_after>/* Dice Physics
 * this is open source, please refer to the licences for details.
 *
 * Dice Physics is designed to replace Math.random() with the physics
 * of actual dicerolls.
 * Dice rolls are not purely random, they are complex Neutonian
 * near-chaotic systems.
 *
 * The calculations here should emulate real physics in three instances:
 * 1. The shaking of the die in the hand.
 * 2. The trajectory and spin of the die as it leaves the hand.
 * 3. The bounce as it hits the table and comes to a rest.
 *
 * It will need to accept as input:
 * 1. The initial facing of the die before it is thrown.
 * 2. The shake data from the accelorameter to determine the spin.
 * 3. The release data from the accelorameter to determine trajectory
 *    and more importantly, The vector of contact with the virtual table.
 *
 * additional variables:
 * The distance of the throw from the table.
 * The weight of the die.
 * The friction coeficient based on material of the die and table.
 *
 * It will return the die's upward face as the result of the roll.
 *
 * Settable constants such as height from,
 * and friction coeficient of the virtual table will also play roles.
 *
 * This may have aplications beyond playing games.
 */

//todo: research accelorometers. What output to they give?
//This little beast needs to be designed to eat that kind of output
//and poop out a dice roll.

//todo: research physics formulae,
//and how other games derrive their realistic-looking physics
<|endoftext|>


<commit_before>var _ = require('underscore');
var fs = require('fs');
var path = require('path');
var util = require('util');
var events = require('events');
var zmq = require('zmq');
var messenger = require('messenger');
var WebSocketServer = require('ws').Server;
var config = require('./config');

function SnapshotServer(opt) {
	this._taskCount = 0;
	this._workers = [];
	this._workerConnections = [];
	this._workerIdle = {};
	this._wsServer = null;
	this._opt = _.extend({}, config.defaultArgv, opt);
	this._start();
}

util.inherits(SnapshotServer, events.EventEmitter);

SnapshotServer.prototype = _.extend(SnapshotServer.prototype, {
	_start: function() {
		this._startWsServer();
		for(var i = 0; i < this._opt.workers; i++) {
			this._startWorker(i);
		}
		this._startMsServer();
	},

	_startMsServer: function() {
		var that = this;
		var msServer = this._msServer = messenger.createListener(this._opt.msPort);
		msServer.on('snapshot', function(client, task) {
			console.log('Messenger Server received snapshot request');
			that._dispatchTask(task, function(res) {
				client.reply({
					status: res.status,
					data: res.data || {}
				});
			});
		});
	},

	_startWsServer: function() {
		var that = this;
		this._wsServer = new WebSocketServer({port: this._opt.wsPort}, function() {
			console.log('Web-Socket Server listening on port ' + that._opt.wsPort);
		}).on('connection', function(conn) {
			conn.on('message', function(msg) {
				msg = JSON.parse(msg);
				if(that._wsMessageHandler[msg.type]) {
					that._wsMessageHandler[msg.type].call(that, msg, conn);
				} else {
					console.log('Web-Socket Server unknown message type: ' + msg.type);
				}
			});
			conn.on('close', function() {
				console.log('Phantomjs worker connection closed');
			});
		});
	},

	_startWorker: function(id) {
		var that = this;
		var cwd = process.cwd();
		var out = fs.openSync(path.join(cwd, 'phantomjs-out.log'), 'a');
		var err = fs.openSync(path.join(cwd, 'phantomjs-err.log'), 'a');
		var worker = require('child_process').spawn('phantomjs', [__dirname + '/phantomjs-worker.js', this._opt.wsPort, id], {stdio: ['ignore', out, err]});
		worker.on('close', function(code) {
			console.log('Phantomjs worker ' + id + ' exited. code is ' + code);
			that._startWorker(id);
		});
		if(this._workers[id]) {
			this._workers.splice(id, 1, worker);
			console.log('Phantomjs worker ' + id + ' restarted');
		} else {
			this._workers[id] = worker;
			console.log('Phantomjs worker ' + id + ' started');
		}
	},

	_dispatchTask: function(task, callback) {
		// the callback will be surely called
		var that = this;
		var workerId, conn;
		task.id = this._taskCount++;
		// select a idel worker
		for(var i = 0; i < this._workerConnections.length; i++) {
			if(this._workerIdle[i]) {
				workerId = i;
			}
		}
		// if no idle worker, select one randomly
		if(!(workerId >= 0)) {
			workerId = Math.floor(this._workerConnections.length * Math.random());
		}
		this._workerIdle[workerId] = false;
		conn = this._workerConnections[workerId];
		conn && conn.send(JSON.stringify({
			type: 'task',
			task: task
		}));
		console.log('Web-Socket Server dispatched task ' + task.id + ' to Phantomjs worker ' + workerId);
		var taskCallback = function(msg) {
			called = true;
			console.log('Phantomjs worker ' + msg.workerId + ' task ' + msg.task.id + ' ' + msg.task.status);
			callback(msg.task);
		};
		var called = false;
		this.once('task' + task.id, taskCallback);
		setTimeout(function() {
			if(!called) {
				that.removeListener('task' + task.id, taskCallback);
				taskCallback({msg: 'task', workerId: workerId, task: {
					id: task.id,
					status: 'timeout'
				}});
			}
		}, config.timeout);
	},

	_wsMessageHandler: {
		connected: function(msg, conn) {
			var id = msg.workerId;
			if(this._workerConnections[id]) {
				this._workerConnections.splice(id, 1, conn);
				console.log('Phantomjs worker ' + id + ' reconnected');
			} else {
				this._workerConnections[id] = conn;
				console.log('Phantomjs worker ' + id + ' connected');
			}
		},

		task: function(msg, conn) {
			// worker will report its idle status by the way
			this._workerIdle[msg.workerId] = !!msg.idle;
			this.emit('task' + msg.task.id, msg);
		}
	},

	stop: function() {
		this._workers.forEach(function(worker) {
			worker.kill();
		});
		this._wsServer.close();
	}
});

exports.SnapshotServer = SnapshotServer;
<commit_msg>update<commit_after>var _ = require('underscore');
var fs = require('fs');
var path = require('path');
var util = require('util');
var events = require('events');
var zmq = require('zmq');
var messenger = require('messenger');
var WebSocketServer = require('ws').Server;
var config = require('./config');

function SnapshotServer(opt) {
	this._taskCount = 0;
	this._workers = [];
	this._workerConnections = [];
	this._workerIdle = {};
	this._wsServer = null;
	this._opt = _.extend({}, config.defaultArgv, opt);
	this._start();
}

util.inherits(SnapshotServer, events.EventEmitter);

SnapshotServer.prototype = _.extend(SnapshotServer.prototype, {
	_start: function() {
		this._startWsServer();
		for(var i = 0; i < this._opt.workers; i++) {
			this._startWorker(i);
		}
		this._startMsServer();
	},

	_startMsServer: function() {
		var that = this;
		var msServer = this._msServer = messenger.createListener(this._opt.msPort);
		msServer.on('snapshot', function(client, task) {
			console.log('Messenger Server received snapshot request');
			that._dispatchTask(task, function(res) {
				client.reply({
					status: res.status,
					data: res.data || {}
				});
			});
		});
	},

	_startWsServer: function() {
		var that = this;
		this._wsServer = new WebSocketServer({port: this._opt.wsPort}, function() {
			console.log('Web-Socket Server listening on port ' + that._opt.wsPort);
		}).on('connection', function(conn) {
			conn.on('message', function(msg) {
				msg = JSON.parse(msg);
				if(that._wsMessageHandler[msg.type]) {
					that._wsMessageHandler[msg.type].call(that, msg, conn);
				} else {
					console.log('Web-Socket Server unknown message type: ' + msg.type);
				}
			});
			conn.on('close', function() {
				console.log('Phantomjs worker connection closed');
			});
		});
	},

	_startWorker: function(id) {
		var that = this;
		var cwd = process.cwd();
		var out = fs.openSync(path.join(cwd, 'phantomjs-out.log'), 'a');
		var err = fs.openSync(path.join(cwd, 'phantomjs-err.log'), 'a');
		var worker = require('child_process').spawn('phantomjs', [__dirname + '/phantomjs-worker.js', this._opt.wsPort, id], {stdio: ['ignore', out, err]});
		worker.on('close', function(code) {
			console.log('Phantomjs worker ' + id + ' exited. code is ' + code);
			that._startWorker(id);
		});
		if(this._workers[id]) {
			this._workers.splice(id, 1, worker);
			console.log('Phantomjs worker ' + id + ' restarted');
		} else {
			this._workers[id] = worker;
			console.log('Phantomjs worker ' + id + ' started');
		}
		this._workerIdle[id] = true;
	},

	_dispatchTask: function(task, callback) {
		// the callback will be surely called
		var that = this;
		var workerId, conn;
		task.id = this._taskCount++;
		// select a idel worker
		for(var i = 0; i < this._workerConnections.length; i++) {
			if(this._workerIdle[i]) {
				workerId = i;
			}
		}
		// if no idle worker, select one randomly
		if(!(workerId >= 0)) {
			workerId = Math.floor(this._workerConnections.length * Math.random());
		}
		this._workerIdle[workerId] = false;
		conn = this._workerConnections[workerId];
		conn && conn.send(JSON.stringify({
			type: 'task',
			task: task
		}));
		console.log('Web-Socket Server dispatched task ' + task.id + ' to Phantomjs worker ' + workerId);
		var taskCallback = function(msg) {
			called = true;
			console.log('Phantomjs worker ' + msg.workerId + ' task ' + msg.task.id + ' ' + msg.task.status);
			callback(msg.task);
		};
		var called = false;
		this.once('task' + task.id, taskCallback);
		setTimeout(function() {
			if(!called) {
				that.removeListener('task' + task.id, taskCallback);
				taskCallback({msg: 'task', workerId: workerId, task: {
					id: task.id,
					status: 'timeout'
				}});
			}
		}, config.timeout);
	},

	_wsMessageHandler: {
		connected: function(msg, conn) {
			var id = msg.workerId;
			if(this._workerConnections[id]) {
				this._workerConnections.splice(id, 1, conn);
				console.log('Phantomjs worker ' + id + ' reconnected');
			} else {
				this._workerConnections[id] = conn;
				console.log('Phantomjs worker ' + id + ' connected');
			}
		},

		task: function(msg, conn) {
			// worker will report its idle status by the way
			this._workerIdle[msg.workerId] = !!msg.idle;
			this.emit('task' + msg.task.id, msg);
		}
	},

	stop: function() {
		this._workers.forEach(function(worker) {
			worker.kill();
		});
		this._wsServer.close();
	}
});

exports.SnapshotServer = SnapshotServer;
<|endoftext|>


<commit_before>function TiledCanvas (canvas, settings) {
    this.canvas = canvas;
    this.ctx = this.canvas.getContext('2d');

    this.leftTopX = 0;
    this.leftTopY = 0;
    this.zoom = 1; // 2 = two times zoomed in

    this.affecting = [[0, 0], [0, 0]];
    this.chunks = {};
    // this.chunks[chunkX][chunkY] is a context or 'empty'

    this.settings = this.normalizeDefaults(settings, this.defaultSettings);
    this.contextQueue = [];
    this.context = this.createContext();
}

TiledCanvas.prototype.defaultSettings = {
    chunkSize: 256
};

TiledCanvas.prototype.cloneObject = function (obj) {
	var clone = {};
	for (var k in obj) {
		if (typeof obj[k] === "object" && !(obj[k] instanceof Array)) {
			clone[k] = this.cloneObject(obj[k]);
		} else {
			clone[k] = obj[k]
		}
	}
	return clone;
};

TiledCanvas.prototype.normalizeDefaults = function normalizeDefaults (target, defaults) {
	target = target || {};
	var normalized = this.cloneObject(target);
	for (var k in defaults) {
		if (typeof defaults[k] === "object" && !(defaults[k] instanceof Array)) {
			normalized[k] = this.normalizeDefaults(target[k] || {}, defaults[k]);
		} else {
			normalized[k] = target[k] || defaults[k];
		}
	}
	return normalized;
};

TiledCanvas.prototype.redraw = function redraw (noclear) {
    if (!noclear) this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);

    var startChunkX = Math.floor(this.leftTopX / this.settings.chunkSize),
        endChunkX   = Math.ceil((this.leftTopX + this.canvas.width / this.zoom) / this.settings.chunkSize),
        startChunkY = Math.floor(this.leftTopY / this.settings.chunkSize),
        endChunkY   = Math.ceil((this.leftTopY + this.canvas.height / this.zoom) / this.settings.chunkSize);

    for (var chunkX = startChunkX; chunkX < endChunkX; chunkX++) {
        for (var chunkY = startChunkY; chunkY < endChunkY; chunkY++) {
            this.drawChunk(chunkX, chunkY);
        }
    }
};

TiledCanvas.prototype.drawChunk = function drawChunk (chunkX, chunkY) {
    if (this.chunks[chunkX] && this.chunks[chunkX][chunkY] && this.chunks[chunkX][chunkY] !== "empty") {
        this.ctx.drawImage(this.chunks[chunkX][chunkY].canvas, ((chunkX * this.settings.chunkSize) - this.leftTopX) * this.zoom, ((chunkY * this.settings.chunkSize) - this.leftTopY) * this.zoom, this.settings.chunkSize * this.zoom, this.settings.chunkSize * this.zoom);
    } else if(typeof this.requestUserChunk == "function" && (!this.chunks[chunkX] || this.chunks[chunkX][chunkY] !== "empty")) {
        this.requestChunk(chunkX, chunkY);
    }
};

TiledCanvas.prototype.goto = function goto (x, y) {
    this.leftTopX = x;
    this.leftTopY = y;
    this.redraw();
};

TiledCanvas.prototype.relativeZoom = function relativeZoom (zoom) {
    this.zoom *= zoom;
    this.redraw();
};

TiledCanvas.prototype.absoluteZoom = function absoluteZoom (zoom) {
    this.zoom = zoom;
    this.redraw();
};

TiledCanvas.prototype.execute = function execute () {
    this.executeNoRedraw();
    this.redraw();
};

TiledCanvas.prototype.executeNoRedraw = function executeNoRedraw () {
    for (var chunkX = this.affecting[0][0]; chunkX < this.affecting[1][0]; chunkX++) {
        for (var chunkY = this.affecting[0][1]; chunkY < this.affecting[1][1]; chunkY++) {
            this.executeChunk(chunkX, chunkY);
        }
    }
    this.contextQueue = [];
};

TiledCanvas.prototype.clearAll = function clearAll () {
    this.contextQueue = [];
    for (var chunkX in this.chunks) {
        this.clearChunkRow(chunkX);
    }
};

TiledCanvas.prototype.clearChunkRow = function clearChunkRow (chunkX) {
    for (var chunkY in this.chunks[chunkX]) {
        this.clearChunk(chunkX, chunkY);
    }
};

TiledCanvas.prototype.clearChunk = function clearChunk (chunkX, chunkY) {
    if (this.chunks[chunkX][chunkY] == "empty") return;
	this.chunks[chunkX][chunkY].clearRect(chunkX * this.settings.chunkSize, chunkY * this.settings.chunkSize, this.chunks[chunkX][chunkY].canvas.width, this.chunks[chunkX][chunkY].canvas.height);
};

TiledCanvas.prototype.requestChunk = function requestChunk (chunkX, chunkY, callback) {
    // Request a chunk and redraw once we got it
    if (typeof this.requestUserChunk !== "function") return;
    callback = callback || function () {};
    this.requestChunkCallbackList = this.requestChunkCallbackList || {};

    if (this.requestChunkCallbackList[chunkX] && this.requestChunkCallbackList[chunkX][chunkY]) {
        // This chunk has already been requested, add to the callback list
        this.requestChunkCallbackList[chunkX][chunkY].push(callback);
    } else {
        // Create a callback list for this chunk
        this.requestChunkCallbackList[chunkX] = this.requestChunkCallbackList[chunkX] || {};
        this.requestChunkCallbackList[chunkX][chunkY] = [callback];

        this.requestUserChunk(chunkX, chunkY, function (image) {
            // For responsiveness make sure the callback doesnt happen in the same event frame
            setTimeout(this.setUserChunk.bind(this, chunkX, chunkY, image));
        }.bind(this));
    }
};

TiledCanvas.prototype.setUserChunk = function setUserChunk (chunkX, chunkY, image) {
    // Don't set the user chunk twice
    if (this.chunks[chunkX] && this.chunks[chunkX][chunkY]) return;

    // If the image is falsy and there is no queue then this chunk is transparent
    // for performance reasons empty chunks should not allocate memory
    if (!image) {
        this.chunks[chunkX] = this.chunks[chunkX] || {};
        this.chunks[chunkX][chunkY] = "empty";
        return;
    }

    // Draw the chunk
    this.chunks[chunkX] = this.chunks[chunkX] || {};
    this.chunks[chunkX][chunkY] =  this.newCtx(this.settings.chunkSize, this.settings.chunkSize, -chunkX * this.settings.chunkSize, -chunkY * this.settings.chunkSize);
    this.chunks[chunkX][chunkY].drawImage(image, chunkX * this.settings.chunkSize, chunkY * this.settings.chunkSize);

    // Run all callbacks
    var callbackList = this.requestChunkCallbackList[chunkX][chunkY];
    for (var k = 0; k < callbackList.length; k++) {
        callbackList[k]();
    }

    // Do a full redraw of the tiled canvas
    this.redraw();

    delete this.requestChunkCallbackList[chunkX][chunkY];
};

TiledCanvas.prototype.copyArray = function copyArray (arr) {
    var temp = [];
    for (var k = 0; k < arr.length; k++) {
        temp[k] = arr[k];
    }
    return temp;
};

TiledCanvas.prototype.executeChunk = function executeChunk (chunkX, chunkY, queue) {
    // Executes the current queue on a chunk
    // If queue is set execute that queue instead
    this.chunks[chunkX] = this.chunks[chunkX] || [];

    if (!this.chunks[chunkX][chunkY] || this.chunks[chunkX][chunkY] == "empty") {
        // This chunk has never been painted to before
        // We first have to ask what this chunk looks like
        // Remember the Queue untill we got the chunk
        // if we already remembered a queue then add this queue to it
        // Only do this when we actually want to use userdefined chunks
        if (typeof this.requestUserChunk == "function" && this.chunks[chunkX][chunkY] !== "empty") {
            this.requestChunk(chunkX, chunkY, function (queue) {
                this.executeChunk(chunkX, chunkY, queue);
            }.bind(this, this.copyArray(queue || this.contextQueue)))
            return;
        } else {
            this.chunks[chunkX][chunkY] =  this.newCtx(this.settings.chunkSize, this.settings.chunkSize, -chunkX * this.settings.chunkSize, -chunkY * this.settings.chunkSize);
        }
    }

    var ctx = this.chunks[chunkX][chunkY];
    var queue = queue || this.contextQueue;

    for (var queuekey = 0; queuekey < queue.length; queuekey++) {
        if (typeof ctx[queue[queuekey][0]] === 'function') {
            this.executeQueueOnChunk(ctx, queue[queuekey]);
        } else {
            ctx[queue[queuekey][0]] = queue[queuekey][1];
        }
    }
};

TiledCanvas.prototype.executeQueueOnChunk = function executeQueueOnChunk (ctx, args) {
    ctx[args[0]].apply(ctx, Array.prototype.slice.call(args, 1));
};

TiledCanvas.prototype.drawingRegion = function (startX, startY, endX, endY, border) {
    border = border || 0;
    this.affecting[0][0] = Math.floor((Math.min(startX, endX) - border) / this.settings.chunkSize);
    this.affecting[0][1] = Math.floor((Math.min(startY, endY) - border) / this.settings.chunkSize);
    this.affecting[1][0] = Math.ceil((Math.max(endX, startX) + border) / this.settings.chunkSize);
    this.affecting[1][1] = Math.ceil((Math.max(endY, startY) + border) / this.settings.chunkSize);
};

TiledCanvas.prototype.newCtx = function newCtx (width, height, translateX, translateY) {
    var ctx = document.createElement('canvas').getContext('2d');
    ctx.canvas.width = width;
    ctx.canvas.height = height;
    ctx.translate(translateX, translateY);
    return ctx;
};

TiledCanvas.prototype.createContext = function createContext () {
    var context = {};
    var ctx = document.createElement('canvas').getContext('2d');
    for (var key in ctx) {
        if (typeof ctx[key] === 'function') {
            context[key] = function (func) {
                this.contextQueue.push(arguments);
            }.bind(this, key);
        } else if (typeof ctx[key] !== 'object') {
            context.__defineGetter__(key, function (key) {
                var ctx = this.newCtx();
                for (var queuekey = 0; queuekey < this.contextQueue.length; queuekey++) {
                    if (typeof ctx[args[0]] === 'function') {
                        ctx[args[0]].apply(ctx, args.slice(1));
                    } else {
                        ctx[args[0]] = args[1];
                    }
                }
                return ctx[key];
            }.bind(this, key));

            context.__defineSetter__(key, function (key, value) {
                this.contextQueue.push(arguments);
            }.bind(this, key));
        }
    }
    return context;
};
<commit_msg>Fix bug when userchunk is empty but we have a queue<commit_after>function TiledCanvas (canvas, settings) {
    this.canvas = canvas;
    this.ctx = this.canvas.getContext('2d');

    this.leftTopX = 0;
    this.leftTopY = 0;
    this.zoom = 1; // 2 = two times zoomed in

    this.affecting = [[0, 0], [0, 0]];
    this.chunks = {};
    // this.chunks[chunkX][chunkY] is a context or 'empty'

    this.settings = this.normalizeDefaults(settings, this.defaultSettings);
    this.contextQueue = [];
    this.context = this.createContext();
}

TiledCanvas.prototype.defaultSettings = {
    chunkSize: 256
};

TiledCanvas.prototype.cloneObject = function (obj) {
	var clone = {};
	for (var k in obj) {
		if (typeof obj[k] === "object" && !(obj[k] instanceof Array)) {
			clone[k] = this.cloneObject(obj[k]);
		} else {
			clone[k] = obj[k]
		}
	}
	return clone;
};

TiledCanvas.prototype.normalizeDefaults = function normalizeDefaults (target, defaults) {
	target = target || {};
	var normalized = this.cloneObject(target);
	for (var k in defaults) {
		if (typeof defaults[k] === "object" && !(defaults[k] instanceof Array)) {
			normalized[k] = this.normalizeDefaults(target[k] || {}, defaults[k]);
		} else {
			normalized[k] = target[k] || defaults[k];
		}
	}
	return normalized;
};

TiledCanvas.prototype.redraw = function redraw (noclear) {
    if (!noclear) this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);

    var startChunkX = Math.floor(this.leftTopX / this.settings.chunkSize),
        endChunkX   = Math.ceil((this.leftTopX + this.canvas.width / this.zoom) / this.settings.chunkSize),
        startChunkY = Math.floor(this.leftTopY / this.settings.chunkSize),
        endChunkY   = Math.ceil((this.leftTopY + this.canvas.height / this.zoom) / this.settings.chunkSize);

    for (var chunkX = startChunkX; chunkX < endChunkX; chunkX++) {
        for (var chunkY = startChunkY; chunkY < endChunkY; chunkY++) {
            this.drawChunk(chunkX, chunkY);
        }
    }
};

TiledCanvas.prototype.drawChunk = function drawChunk (chunkX, chunkY) {
    if (this.chunks[chunkX] && this.chunks[chunkX][chunkY] && this.chunks[chunkX][chunkY] !== "empty") {
        this.ctx.drawImage(this.chunks[chunkX][chunkY].canvas, ((chunkX * this.settings.chunkSize) - this.leftTopX) * this.zoom, ((chunkY * this.settings.chunkSize) - this.leftTopY) * this.zoom, this.settings.chunkSize * this.zoom, this.settings.chunkSize * this.zoom);
    } else if(typeof this.requestUserChunk == "function" && (!this.chunks[chunkX] || this.chunks[chunkX][chunkY] !== "empty")) {
        this.requestChunk(chunkX, chunkY);
    }
};

TiledCanvas.prototype.goto = function goto (x, y) {
    this.leftTopX = x;
    this.leftTopY = y;
    this.redraw();
};

TiledCanvas.prototype.relativeZoom = function relativeZoom (zoom) {
    this.zoom *= zoom;
    this.redraw();
};

TiledCanvas.prototype.absoluteZoom = function absoluteZoom (zoom) {
    this.zoom = zoom;
    this.redraw();
};

TiledCanvas.prototype.execute = function execute () {
    this.executeNoRedraw();
    this.redraw();
};

TiledCanvas.prototype.executeNoRedraw = function executeNoRedraw () {
    for (var chunkX = this.affecting[0][0]; chunkX < this.affecting[1][0]; chunkX++) {
        for (var chunkY = this.affecting[0][1]; chunkY < this.affecting[1][1]; chunkY++) {
            this.executeChunk(chunkX, chunkY);
        }
    }
    this.contextQueue = [];
};

TiledCanvas.prototype.clearAll = function clearAll () {
    this.contextQueue = [];
    for (var chunkX in this.chunks) {
        this.clearChunkRow(chunkX);
    }
};

TiledCanvas.prototype.clearChunkRow = function clearChunkRow (chunkX) {
    for (var chunkY in this.chunks[chunkX]) {
        this.clearChunk(chunkX, chunkY);
    }
};

TiledCanvas.prototype.clearChunk = function clearChunk (chunkX, chunkY) {
    if (this.chunks[chunkX][chunkY] == "empty") return;
	this.chunks[chunkX][chunkY].clearRect(chunkX * this.settings.chunkSize, chunkY * this.settings.chunkSize, this.chunks[chunkX][chunkY].canvas.width, this.chunks[chunkX][chunkY].canvas.height);
};

TiledCanvas.prototype.requestChunk = function requestChunk (chunkX, chunkY, callback) {
    // Request a chunk and redraw once we got it
    if (typeof this.requestUserChunk !== "function") return;
    this.requestChunkCallbackList = this.requestChunkCallbackList || {};

    if (this.requestChunkCallbackList[chunkX] && this.requestChunkCallbackList[chunkX][chunkY]) {
        if (!callback) return;
        // This chunk has already been requested, add to the callback list
        this.requestChunkCallbackList[chunkX][chunkY].push(callback);
    } else {
        this.requestChunkCallbackList[chunkX] = this.requestChunkCallbackList[chunkX] || {};

        if (callback) {
            // Create a callback list for this chunk
            this.requestChunkCallbackList[chunkX][chunkY] = [callback];
        } else {
            this.requestChunkCallbackList[chunkX][chunkY] = [];
        }

        this.requestUserChunk(chunkX, chunkY, function (image) {
            // For responsiveness make sure the callback doesnt happen in the same event frame
            setTimeout(this.setUserChunk.bind(this, chunkX, chunkY, image));
        }.bind(this));
    }
};

TiledCanvas.prototype.setUserChunk = function setUserChunk (chunkX, chunkY, image) {
    // Don't set the user chunk twice
    if (this.chunks[chunkX] && this.chunks[chunkX][chunkY]) return;

    // If the image is falsy and there is no queue then this chunk is transparent
    // for performance reasons empty chunks should not allocate memory
    if (!image && (!this.requestChunkCallbackList[chunkX] || this.requestChunkCallbackList[chunkX][chunkY].lenth == 0)) {
        this.chunks[chunkX] = this.chunks[chunkX] || {};
        this.chunks[chunkX][chunkY] = "empty";
        return;
    }

    // Draw the chunk
    this.chunks[chunkX] = this.chunks[chunkX] || {};
    this.chunks[chunkX][chunkY] =  this.newCtx(this.settings.chunkSize, this.settings.chunkSize, -chunkX * this.settings.chunkSize, -chunkY * this.settings.chunkSize);
    this.chunks[chunkX][chunkY].drawImage(image, chunkX * this.settings.chunkSize, chunkY * this.settings.chunkSize);

    // Run all callbacks
    var callbackList = this.requestChunkCallbackList[chunkX][chunkY];
    for (var k = 0; k < callbackList.length; k++) {
        callbackList[k]();
    }

    // Do a full redraw of the tiled canvas
    this.redraw();

    delete this.requestChunkCallbackList[chunkX][chunkY];
};

TiledCanvas.prototype.copyArray = function copyArray (arr) {
    var temp = [];
    for (var k = 0; k < arr.length; k++) {
        temp[k] = arr[k];
    }
    return temp;
};

TiledCanvas.prototype.executeChunk = function executeChunk (chunkX, chunkY, queue) {
    // Executes the current queue on a chunk
    // If queue is set execute that queue instead
    this.chunks[chunkX] = this.chunks[chunkX] || [];

    if (!this.chunks[chunkX][chunkY] || this.chunks[chunkX][chunkY] == "empty") {
        // This chunk has never been painted to before
        // We first have to ask what this chunk looks like
        // Remember the Queue untill we got the chunk
        // if we already remembered a queue then add this queue to it
        // Only do this when we actually want to use userdefined chunks
        if (typeof this.requestUserChunk == "function" && this.chunks[chunkX][chunkY] !== "empty") {
            this.requestChunk(chunkX, chunkY, function (queue) {
                this.executeChunk(chunkX, chunkY, queue);
            }.bind(this, this.copyArray(queue || this.contextQueue)))
            return;
        } else {
            this.chunks[chunkX][chunkY] =  this.newCtx(this.settings.chunkSize, this.settings.chunkSize, -chunkX * this.settings.chunkSize, -chunkY * this.settings.chunkSize);
        }
    }

    var ctx = this.chunks[chunkX][chunkY];
    var queue = queue || this.contextQueue;

    for (var queuekey = 0; queuekey < queue.length; queuekey++) {
        if (typeof ctx[queue[queuekey][0]] === 'function') {
            this.executeQueueOnChunk(ctx, queue[queuekey]);
        } else {
            ctx[queue[queuekey][0]] = queue[queuekey][1];
        }
    }
};

TiledCanvas.prototype.executeQueueOnChunk = function executeQueueOnChunk (ctx, args) {
    ctx[args[0]].apply(ctx, Array.prototype.slice.call(args, 1));
};

TiledCanvas.prototype.drawingRegion = function (startX, startY, endX, endY, border) {
    border = border || 0;
    this.affecting[0][0] = Math.floor((Math.min(startX, endX) - border) / this.settings.chunkSize);
    this.affecting[0][1] = Math.floor((Math.min(startY, endY) - border) / this.settings.chunkSize);
    this.affecting[1][0] = Math.ceil((Math.max(endX, startX) + border) / this.settings.chunkSize);
    this.affecting[1][1] = Math.ceil((Math.max(endY, startY) + border) / this.settings.chunkSize);
};

TiledCanvas.prototype.newCtx = function newCtx (width, height, translateX, translateY) {
    var ctx = document.createElement('canvas').getContext('2d');
    ctx.canvas.width = width;
    ctx.canvas.height = height;
    ctx.translate(translateX, translateY);
    return ctx;
};

TiledCanvas.prototype.createContext = function createContext () {
    var context = {};
    var ctx = document.createElement('canvas').getContext('2d');
    for (var key in ctx) {
        if (typeof ctx[key] === 'function') {
            context[key] = function (func) {
                this.contextQueue.push(arguments);
            }.bind(this, key);
        } else if (typeof ctx[key] !== 'object') {
            context.__defineGetter__(key, function (key) {
                var ctx = this.newCtx();
                for (var queuekey = 0; queuekey < this.contextQueue.length; queuekey++) {
                    if (typeof ctx[args[0]] === 'function') {
                        ctx[args[0]].apply(ctx, args.slice(1));
                    } else {
                        ctx[args[0]] = args[1];
                    }
                }
                return ctx[key];
            }.bind(this, key));

            context.__defineSetter__(key, function (key, value) {
                this.contextQueue.push(arguments);
            }.bind(this, key));
        }
    }
    return context;
};
<|endoftext|>


<commit_before>import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import WsClient from '../../WsClient';
import { saveUcast } from './PrihlaseniActions';

const successfulResponseSaveUdaje = {
  code: 'ok',
  response: {
    id: '===id==='
  },
  requestId: '0.9310306652587377'
};

const successfulResponseSavePrihlaska = {
  code: 'ok',
  requestId: '0.9310306652587377'
};

const unsuccessfulResponse = {
  code: 'unfulfilled request',
  status: 'A strange error occurred.'
};

const authTokenInvalidResponse = {
  code: 'authentication token invalid',
  status: 'Neplatný ověřovací token.'
};

let responseNumber;
beforeEach(() => {
  responseNumber = 0;
});

const mockWsClient = new WsClient();
let responses = [];
// eslint-disable-next-line no-plusplus
mockWsClient.sendRequest = async () => responses[responseNumber++];

const middlewares = [thunk.withExtraArgument(mockWsClient)];
const mockStore = configureStore(middlewares);

it('saveUcast() should dispatch four successful actions', async () => {
  responses = [successfulResponseSaveUdaje, successfulResponseSavePrihlaska];
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { validateForm: false, udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_FORM' });
  expect(actions[1]).toEqual({ type: 'PRIHLASENI_SAVE_REQUEST' });
  expect(actions[2]).toEqual(
    expect.objectContaining({
      id: '===id===',
      type: 'PRIHLASENI_SAVE_SUCCESS'
    })
  );
  expect(actions[3]).toEqual({ type: 'PRIHLASENI_SAVE_SHOW_MODAL' });
});

it('saveUcast() should dispatch two unsuccessful actions 1/2', async () => {
  responses = [unsuccessfulResponse, unsuccessfulResponse];
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_FORM' });
  expect(actions[1]).toEqual({ type: 'PRIHLASENI_SAVE_REQUEST' });
  expect(actions[2]).toEqual(
    expect.objectContaining({
      type: 'PRIHLASENI_SAVE_ERROR',
      code: 'unfulfilled request',
      status: 'A strange error occurred.'
    })
  );
});

it('saveUcast() should dispatch two unsuccessful actions 2/2', async () => {
  responses = [successfulResponseSaveUdaje, unsuccessfulResponse];
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_FORM' });
  expect(actions[1]).toEqual({ type: 'PRIHLASENI_SAVE_REQUEST' });
  expect(actions[2]).toEqual(
    expect.objectContaining({
      type: 'PRIHLASENI_SAVE_ERROR',
      code: 'unfulfilled request',
      status: 'A strange error occurred.'
    })
  );
});

it('saveUcast() should dispatch two unsuccessful actions on an invalid token', async () => {
  responses = [authTokenInvalidResponse, unsuccessfulResponse];
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_FORM' });
  expect(actions[1]).toEqual({ type: 'PRIHLASENI_SAVE_REQUEST' });
  expect(actions[2]).toEqual(
    expect.objectContaining({
      type: 'SIGN_IN_ERROR',
      code: 'authentication token invalid',
      status: 'Platnost ověřovacího tokenu pravděpodobně vypršela. Neplatný ověřovací token.'
    })
  );
});

it('saveUcast() should dispatch validation error', async () => {
  responses = [successfulResponseSaveUdaje, successfulResponseSavePrihlaska];
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { validateForm: true, udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_FORM' });
  expect(actions[1]).toEqual({
    type: 'PRIHLASENI_FORM_INVALID',
    code: 'nejde uložit',
    status: 'Přihláška nejde uložit. Povinná pole nejsou vyplněna.'
  });
});

/* Beware: overrides mockWsClient.sendRequest! */
it('saveUcast() should dispatch two unsuccessful actions on error', async () => {
  mockWsClient.sendRequest = async () => Promise.reject(new Error('Parse error!'));
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_FORM' });
  expect(actions[1]).toEqual({ type: 'PRIHLASENI_SAVE_REQUEST' });
  expect(actions[2]).toEqual(
    expect.objectContaining({
      type: 'PRIHLASENI_SAVE_ERROR',
      code: 'internal error',
      err: new Error('Parse error!')
    })
  );
});

/* Beware: overrides mockWsClient.sendRequest! */
it('saveUcast() should use auth token if available', async () => {
  const tokenSent = { tokenSent: false };
  mockWsClient.sendRequest = async token => {
    if (token) {
      tokenSent.tokenSent = true;
    }
  };
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  expect(tokenSent.tokenSent).toBe(true);
});
<commit_msg>Add tests for addValidatedPlatba().<commit_after>import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import WsClient from '../../WsClient';
import { addValidatedPlatba, saveUcast } from './PrihlaseniActions';

const successfulResponseSaveUdaje = {
  code: 'ok',
  response: {
    id: '===id==='
  },
  requestId: '0.9310306652587377'
};

const successfulResponseSavePrihlaska = {
  code: 'ok',
  requestId: '0.9310306652587377'
};

const unsuccessfulResponse = {
  code: 'unfulfilled request',
  status: 'A strange error occurred.'
};

const authTokenInvalidResponse = {
  code: 'authentication token invalid',
  status: 'Neplatný ověřovací token.'
};

let responseNumber;
beforeEach(() => {
  responseNumber = 0;
});

const mockWsClient = new WsClient();
let responses = [];
// eslint-disable-next-line no-plusplus
mockWsClient.sendRequest = async () => responses[responseNumber++];

const middlewares = [thunk.withExtraArgument(mockWsClient)];
const mockStore = configureStore(middlewares);

it('saveUcast() should dispatch four successful actions', async () => {
  responses = [successfulResponseSaveUdaje, successfulResponseSavePrihlaska];
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { validateForm: false, udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_FORM' });
  expect(actions[1]).toEqual({ type: 'PRIHLASENI_SAVE_REQUEST' });
  expect(actions[2]).toEqual(
    expect.objectContaining({
      id: '===id===',
      type: 'PRIHLASENI_SAVE_SUCCESS'
    })
  );
  expect(actions[3]).toEqual({ type: 'PRIHLASENI_SAVE_SHOW_MODAL' });
});

it('saveUcast() should dispatch two unsuccessful actions 1/2', async () => {
  responses = [unsuccessfulResponse, unsuccessfulResponse];
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_FORM' });
  expect(actions[1]).toEqual({ type: 'PRIHLASENI_SAVE_REQUEST' });
  expect(actions[2]).toEqual(
    expect.objectContaining({
      type: 'PRIHLASENI_SAVE_ERROR',
      code: 'unfulfilled request',
      status: 'A strange error occurred.'
    })
  );
});

it('saveUcast() should dispatch two unsuccessful actions 2/2', async () => {
  responses = [successfulResponseSaveUdaje, unsuccessfulResponse];
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_FORM' });
  expect(actions[1]).toEqual({ type: 'PRIHLASENI_SAVE_REQUEST' });
  expect(actions[2]).toEqual(
    expect.objectContaining({
      type: 'PRIHLASENI_SAVE_ERROR',
      code: 'unfulfilled request',
      status: 'A strange error occurred.'
    })
  );
});

it('saveUcast() should dispatch two unsuccessful actions on an invalid token', async () => {
  responses = [authTokenInvalidResponse, unsuccessfulResponse];
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_FORM' });
  expect(actions[1]).toEqual({ type: 'PRIHLASENI_SAVE_REQUEST' });
  expect(actions[2]).toEqual(
    expect.objectContaining({
      type: 'SIGN_IN_ERROR',
      code: 'authentication token invalid',
      status: 'Platnost ověřovacího tokenu pravděpodobně vypršela. Neplatný ověřovací token.'
    })
  );
});

it('saveUcast() should dispatch validation error', async () => {
  responses = [successfulResponseSaveUdaje, successfulResponseSavePrihlaska];
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { validateForm: true, udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_FORM' });
  expect(actions[1]).toEqual({
    type: 'PRIHLASENI_FORM_INVALID',
    code: 'nejde uložit',
    status: 'Přihláška nejde uložit. Povinná pole nejsou vyplněna.'
  });
});

/* Beware: overrides mockWsClient.sendRequest! */
it('saveUcast() should dispatch two unsuccessful actions on error', async () => {
  mockWsClient.sendRequest = async () => Promise.reject(new Error('Parse error!'));
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_FORM' });
  expect(actions[1]).toEqual({ type: 'PRIHLASENI_SAVE_REQUEST' });
  expect(actions[2]).toEqual(
    expect.objectContaining({
      type: 'PRIHLASENI_SAVE_ERROR',
      code: 'internal error',
      err: new Error('Parse error!')
    })
  );
});

/* Beware: overrides mockWsClient.sendRequest! */
it('saveUcast() should use auth token if available', async () => {
  const tokenSent = { tokenSent: false };
  mockWsClient.sendRequest = async token => {
    if (token) {
      tokenSent.tokenSent = true;
    }
  };
  const store = mockStore({
    auth: { token: '===token===' },
    registrator: { prihlaseni: { udaje: { narozeni: {} }, prihlaska: {} } }
  });

  await store.dispatch(saveUcast());
  expect(tokenSent.tokenSent).toBe(true);
});

it('addValidatedPlatba() should dispatch two successful actions', async () => {
  const store = mockStore({
    registrator: {
      prihlaseni: {
        validatePlatba: false,
        novaPlatba: { castka: '200', datum: '2018-05-12T00:00:00Z', typ: 'převodem' }
      }
    }
  });

  await store.dispatch(addValidatedPlatba());
  const actions = store.getActions();
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_PLATBA' });
  expect(actions[1]).toEqual({
    type: 'PRIHLASENI_ADD_PLATBA'
  });
});

it('addValidatedPlatba() should dispatch only one action', async () => {
  const store = mockStore({
    registrator: {
      prihlaseni: {
        validatePlatba: false,
        novaPlatba: { castka: 'rozepsáno', datum: '2018-05-12T00:00:00Z', typ: 'převodem' }
      }
    }
  });

  await store.dispatch(addValidatedPlatba());
  const actions = store.getActions();
  expect(actions).toHaveLength(1);
  expect(actions[0]).toEqual({ type: 'PRIHLASENI_VALIDATE_PLATBA' });
});
<|endoftext|>


<commit_before>/* eslint comma-dangle: "off" */

$(document).ready(() => {
  let query = '';

  function exportCSV() {
    const token = window.sessionStorage.getItem('userInfo-token');
    $.ajax({
      url: `http://localhost:3000/export${query}`,
      type: 'GET',
      headers: {
        'x-access-token': token
      }
    }).done((response) => {
      const data = response.data;
      const filename = response.filename;
      const link = document.createElement('a');
      link.setAttribute('href', data);
      link.setAttribute('download', filename);
      link.click();
    });
  }
  $('#export-button').click(exportCSV);

  $('#query2Checkbox').change(() => {
    const enabled = $('#query2Checkbox').prop('checked');
    $('#query2Attribute').attr('disabled', !enabled);
    $('#query2Comparator').attr('disabled', !enabled);
    $('#query2Value').attr('disabled', !enabled);
  });
  $('#query3Checkbox').change(() => {
    const enabled = $('#query3Checkbox').prop('checked');
    $('#query3Attribute').attr('disabled', !enabled);
    $('#query3Comparator').attr('disabled', !enabled);
    $('#query3Value').attr('disabled', !enabled);
  });
  $('#query4Checkbox').change(() => {
    const enabled = $('#query4Checkbox').prop('checked');
    $('#query4Attribute').attr('disabled', !enabled);
    $('#query4Comparator').attr('disabled', !enabled);
    $('#query4Value').attr('disabled', !enabled);
  });
  $('#query5Checkbox').change(() => {
    const enabled = $('#query5Checkbox').prop('checked');
    $('#query5Attribute').attr('disabled', !enabled);
    $('#query5Comparator').attr('disabled', !enabled);
    $('#query5Value').attr('disabled', !enabled);
  });

  function loadDataTable() {
    const uri = `http://localhost:3000/cases${query}`;
    // console.log(uri);
    const token = window.sessionStorage.getItem('userInfo-token');
    const table = $('#example').DataTable({
      destroy: true,
      ajax: {
        type: 'GET',
        url: uri,
        headers: {
          'x-access-token': token,
        },
        dataSrc: json => json,
      },
      columnDefs: [
        {
          targets: [4, 5, 8, 12],
          render: $.fn.dataTable.render.moment('x', 'Do MMM YY'),
        },
        {
          targets: [0, 1],
          width: '20px',
        },
      ],
      columns: [
        { data: 'drNumber' },
        { data: 'masterDrNumber' },
        { data: 'division' },
        { data: 'bureau' },
        { data: 'dateOccured' },
        { data: 'dateReported' },
        { data: 'weaponUsed[, ]' },
        { data: 'caseStatus' },
        { data: 'caseStatusDate' },
        { data: 'solvabilityFactor' },
        { data: 'reportingDistrict' },
        { data: 'motive[, ]' },
        { data: 'lastModifiedDate' },
        { data: 'lastModifiedBy.email' },
        { data: 'victim.victName.first' },
        { data: 'address' },
        { data: 'suspects[0].suspName.first' },
        {
          targets: -1,
          data: null,
          defaultContent: '<button id=link-to-case>Click!</button>'
        }
      ],
    });

    $('#example tbody').on('click', 'tr', () => {
      if ($(this).hasClass('selected')) {
        $(this).removeClass('selected');
      } else {
        table.$('tr.selected').removeClass('selected');
        $(this).addClass('selected');
      }
    });

    // https://datatables.net/examples/ajax/null_data_source.html
    $('#example tbody').on('click', 'button', () => {
      const data = table.row().data();
      console.log('parent: ', $('.parent'));
      console.log('selected: ', $('.selected'));
      console.log(data);
      document.cookie = `id=${data['_id']}`;
      window.location = '/case';
    });
  }

  // default data table loads with no query
  loadDataTable();

  $('#submit-query').on('click', () => {
    // /cases?drNumber={"lt": 100, "gt": 30}&bureau=OSB
    const a = $('#query1Attribute').val();
    const c = $('#query1Comparator').val();
    const v = $('#query1Value').val();
    if (c === '=') {
      query = `${query}/?${a}${c}${v}`;
    } else {
      query = `${query}${a}={"${c}":${+v}}`;
    }

    if ($('#query2Checkbox').prop('checked')) {
      const A = $('#query2Attribute').val();
      const C = $('#query2Comparator').val();
      const V = $('#query2Value').val();
      if (C === '=') {
        query = query + '&' + A + C + V;
      } else {
        query = `${query}&${A}={"${C}":${+V}}`;
      }
    }

    if ($('#query3Checkbox').prop('checked')) {
      if ($('#query3Comparator').val() === '=') {
        query = query + '&' + $('#query3Attribute').val() + $('#query3Comparator').val() + $('#query3Value').val();
      } else {
        query = `${query}&${$('#query3Attribute').val()}={"${$('#query3Comparator').val()}":${+$('#query3Value').val()}}`;
      }
    }

    if ($('#query4Checkbox').prop('checked')) {
      if ($('#query4Comparator').val() === '=') {
        query = query + '&' + $('#query4Attribute').val() + $('#query4Comparator').val() + $('#query4Value').val();
      } else {
        query = `${query}&${$('#query4Attribute').val()}={"${$('#query4Comparator').val()}":${+$('#query4Value').val()}}`;
      }
    }
    // console.log(query);
    loadDataTable();
  });
});
<commit_msg>In progress commit for push to master<commit_after>/* eslint comma-dangle: "off" */

$(document).ready(() => {
  let query = '';

  function exportCSV() {
    const token = window.sessionStorage.getItem('userInfo-token');
    $.ajax({
      url: `http://localhost:3000/export${query}`,
      type: 'GET',
      headers: {
        'x-access-token': token
      }
    }).done((response) => {
      const data = response.data;
      const filename = response.filename;
      const link = document.createElement('a');
      link.setAttribute('href', data);
      link.setAttribute('download', filename);
      link.click();
    });
  }
  $('#export-button').click(exportCSV);

  $('#query2Checkbox').change(() => {
    const enabled = $('#query2Checkbox').prop('checked');
    $('#query2Attribute').attr('disabled', !enabled);
    $('#query2Comparator').attr('disabled', !enabled);
    $('#query2Value').attr('disabled', !enabled);
  });
  $('#query3Checkbox').change(() => {
    const enabled = $('#query3Checkbox').prop('checked');
    $('#query3Attribute').attr('disabled', !enabled);
    $('#query3Comparator').attr('disabled', !enabled);
    $('#query3Value').attr('disabled', !enabled);
  });
  $('#query4Checkbox').change(() => {
    const enabled = $('#query4Checkbox').prop('checked');
    $('#query4Attribute').attr('disabled', !enabled);
    $('#query4Comparator').attr('disabled', !enabled);
    $('#query4Value').attr('disabled', !enabled);
  });
  $('#query5Checkbox').change(() => {
    const enabled = $('#query5Checkbox').prop('checked');
    $('#query5Attribute').attr('disabled', !enabled);
    $('#query5Comparator').attr('disabled', !enabled);
    $('#query5Value').attr('disabled', !enabled);
  });

  function loadDataTable() {
    const uri = `http://localhost:3000/cases${query}`;
    // console.log(uri);
    const token = window.sessionStorage.getItem('userInfo-token');
    const table = $('#example').DataTable({
      destroy: true,
      ajax: {
        type: 'GET',
        url: uri,
        headers: {
          'x-access-token': token,
        },
        dataSrc: json => json,
      },
      columnDefs: [
        {
          targets: [4, 5, 8, 12],
          render: $.fn.dataTable.render.moment('x', 'Do MMM YY'),
        },
        {
          targets: [0, 1],
          width: '20px',
        },
      ],
      columns: [
        { data: 'drNumber' },
        { data: 'masterDrNumber' },
        { data: 'division' },
        { data: 'bureau' },
        { data: 'dateOccured' },
        { data: 'dateReported' },
        { data: 'weaponUsed[, ]' },
        { data: 'caseStatus' },
        { data: 'caseStatusDate' },
        { data: 'solvabilityFactor' },
        { data: 'reportingDistrict' },
        { data: 'motive[, ]' },
        { data: 'lastModifiedDate' },
        { data: 'lastModifiedBy.email' },
        { data: 'victim.victName.first' },
        { data: 'address' },
        { data: 'suspects[0].suspName.first' },
        {
          targets: -1,
          data: null,
          defaultContent: '<button>Click!</button>'
        }
      ],
    });

    // $('#example tbody').on('click', 'tr', () => {
    //   if ($(this).hasClass('selected')) {
    //     $(this).removeClass('selected');
    //   } else {
    //     table.$('th.selected').removeClass('selected');
    //     $(this).addClass('selected');
    //   }
    // });

    // https://datatables.net/examples/ajax/null_data_source.html
    $('#example tbody').on('click', 'button', () => {
      console.log($('button'));
      if ($('button').hasClass('selected')) {
        $('button').removeClass('selected');
      } else {
        table.$('th.selected').removeClass('selected');
        $('button').addClass('selected');
      }
      console.log($(this));
      const data = table.row('.parent', '.selected').data();
      // console.log('parent: ', $('.parent'));
      // console.log('selected: ', $('.selected'));
      console.log(data);
      document.cookie = `id=${data['_id']}`;
      // window.location = '/case';
    });
  }

  // default data table loads with no query
  loadDataTable();

  $('#submit-query').on('click', () => {
    // /cases?drNumber={"lt": 100, "gt": 30}&bureau=OSB
    const a = $('#query1Attribute').val();
    const c = $('#query1Comparator').val();
    const v = $('#query1Value').val();
    if (c === '=') {
      query = `${query}/?${a}${c}${v}`;
    } else {
      query = `${query}${a}={"${c}":${+v}}`;
    }

    if ($('#query2Checkbox').prop('checked')) {
      const A = $('#query2Attribute').val();
      const C = $('#query2Comparator').val();
      const V = $('#query2Value').val();
      if (C === '=') {
        query = query + '&' + A + C + V;
      } else {
        query = `${query}&${A}={"${C}":${+V}}`;
      }
    }

    if ($('#query3Checkbox').prop('checked')) {
      if ($('#query3Comparator').val() === '=') {
        query = query + '&' + $('#query3Attribute').val() + $('#query3Comparator').val() + $('#query3Value').val();
      } else {
        query = `${query}&${$('#query3Attribute').val()}={"${$('#query3Comparator').val()}":${+$('#query3Value').val()}}`;
      }
    }

    if ($('#query4Checkbox').prop('checked')) {
      if ($('#query4Comparator').val() === '=') {
        query = query + '&' + $('#query4Attribute').val() + $('#query4Comparator').val() + $('#query4Value').val();
      } else {
        query = `${query}&${$('#query4Attribute').val()}={"${$('#query4Comparator').val()}":${+$('#query4Value').val()}}`;
      }
    }
    // console.log(query);
    loadDataTable();
  });
});
<|endoftext|>


<commit_before>/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
regexp:true, undef:true, strict:true, trailing:true, white:true */
/*global X:true */

(function () {
  "use strict";

  var _ = X._;

  /**
    We've stored report data in a temporary table with a key. Return the data to anyone who has the
    appropriate key.

    @extends X.Route
    @class
   */
  X.dataFromKeyRoute = X.Route.create({

    handle: function (xtr) {
      // this is all very temporary
      var that = this,
        querystring = require("querystring"),
        url = xtr.request.url,
        params = url.substring(url.indexOf('?') + 1),
        dataKey = -1,
        dataString = '[{"id": 32,"isActive": true,"name": "Admin Admin","firstName": "Admin","lastName": "Admin","jobTitle": "Admin"}]',
        data = JSON.parse(dataString),
        queryString = '{"orderBy":[{"attribute": "lastName"},{"attribute": "firstName"}],"parameters": [{"attribute": "isActive","operator": "=","value": true}],"recordType": "XM.ContactListItem"}',
        query = JSON.parse(queryString);

      if (params) {
        dataKey = querystring.parse(params).dataKey;
      }

      // TODO: actually fetch real data from the temporary table
      xtr.write({data: data, query: query}).close();
    },

    handles: "dataFromKey /dataFromKey".w()
  });
}());
<commit_msg>issue #19268: sending back data as application slash json<commit_after>/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
regexp:true, undef:true, strict:true, trailing:true, white:true */
/*global X:true */

(function () {
  "use strict";

  var _ = X._;

  /**
    We've stored report data in a temporary table with a key. Return the data to anyone who has the
    appropriate key.

    @extends X.Route
    @class
   */
  X.dataFromKeyRoute = X.Route.create({

    handle: function (xtr) {
      // this is all very temporary
      var that = this,
        querystring = require("querystring"),
        url = xtr.request.url,
        response = xtr.get("response"),
        params = url.substring(url.indexOf('?') + 1),
        dataKey = -1,
        dataString = '[{"id": 32,"isActive": true,"name": "Admin Admin","firstName": "Admin","lastName": "Admin","jobTitle": "Admin"}]',
        data = JSON.parse(dataString),
        queryString = '{"orderBy":[{"attribute": "lastName"},{"attribute": "firstName"}],"parameters": [{"attribute": "isActive","operator": "=","value": true}],"recordType": "XM.ContactListItem"}',
        query = JSON.parse(queryString);

      if (params) {
        dataKey = querystring.parse(params).dataKey;
      }

      // TODO: actually fetch real data from the temporary table
      response.writeHead(200, {"Content-Type": "application/json"});
      response.write(JSON.stringify({data: data, query: query}));
      response.end();
    },

    handles: "dataFromKey /dataFromKey".w()
  });
}());
<|endoftext|>