<commit_before>/*global describe, beforeEach, it*/
'use strict';

var path = require('path');
var assert = require('yeoman-assert');
var helpers = require('yeoman-test');
var fse = require('fs-extra');

const constants = require('../generators/generator-constants'),
    TEST_DIR = constants.TEST_DIR,
    CLIENT_MAIN_SRC_DIR = constants.CLIENT_MAIN_SRC_DIR,
    CLIENT_TEST_SRC_DIR = constants.CLIENT_TEST_SRC_DIR,
    SERVER_MAIN_SRC_DIR = constants.SERVER_MAIN_SRC_DIR,
    SERVER_TEST_SRC_DIR = constants.SERVER_TEST_SRC_DIR;

const expectedFiles = {
    client : [
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foos.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-detail.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-dialog.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-delete-dialog.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo.state.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-dialog.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-delete-dialog.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-detail.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo.service.js',
        CLIENT_TEST_SRC_DIR + 'spec/app/entities/foo/foo-detail.controller.spec.js',
    ],
    clientWithSuffix : [
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foos-management.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management-detail.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management-dialog.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management-delete-dialog.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management.state.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management-dialog.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management-delete-dialog.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management-detail.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo.service.js',
        CLIENT_TEST_SRC_DIR + 'spec/app/entities/foo/foo-management-detail.controller.spec.js',
    ],
    server : [
        '.jhipster/Foo.json',
        SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/domain/Foo.java',
        SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/repository/FooRepository.java',
        SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/web/rest/FooResource.java',
        // SERVER_MAIN_RES_DIR + 'config/liquibase/changelog/20160120213555_added_entity_Foo.xml',
        SERVER_TEST_SRC_DIR + 'com/mycompany/myapp/web/rest/FooResourceIntTest.java',
        TEST_DIR + 'gatling/simulations/FooGatlingTest.scala',
    ]
}
describe('JHipster generator entity', function () {
    describe('no dto, no service, no pagination', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'no',
                    service: 'no',
                    pagination: 'no'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
            assert.file([
                //CLIENT_MAIN_SRC_DIR + 'i18n/en/foo.json', //this should ideally be working
                //CLIENT_MAIN_SRC_DIR + 'i18n/fr/foo.json'
            ]);
        });
    });

    describe('with dto, no service, no pagination', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'mapstruct',
                    service: 'no',
                    pagination: 'no'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
            assert.file([
                SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/web/rest/dto/FooDTO.java',
                SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/web/rest/mapper/FooMapper.java'
            ]);
        });
    });

    describe('no dto, with serviceClass, no pagination', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'no',
                    service: 'serviceClass',
                    pagination: 'no'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
            assert.file([,
                SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/service/FooService.java'
            ]);
        });
    });

    describe('no dto, with serviceImpl, no pagination', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'no',
                    service: 'serviceImpl',
                    pagination: 'no'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
            assert.file([
                SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/service/FooService.java',
                SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/service/impl/FooServiceImpl.java'
            ]);
        });
    });

    describe('no dto, no service, with pager', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'no',
                    service: 'no',
                    pagination: 'pager'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
        });
    });

    describe('no dto, no service, with pagination', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'no',
                    service: 'no',
                    pagination: 'pagination'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
        });
    });

    describe('no dto, no service, with infinite-scroll', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'no',
                    service: 'no',
                    pagination: 'infinite-scroll'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
        });
    });

    describe('with dto, serviceImpl, with hazelcast, elasticsearch and noi18n', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/noi18n'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'yes',
                    service: 'serviceImpl',
                    pagination: 'infinite-scroll'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
            assert.noFile([
                CLIENT_MAIN_SRC_DIR + 'i18n/en/foo.json',
                CLIENT_MAIN_SRC_DIR + 'i18n/fr/foo.json'
            ])
        });
    });

    describe('with angulr suffix', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withOptions({'angular-suffix': 'management'})
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'yes',
                    service: 'serviceImpl',
                    pagination: 'infinite-scroll'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.clientWithSuffix);
        });
    });
});
<commit_msg>add test<commit_after>/*global describe, beforeEach, it*/
'use strict';

var path = require('path');
var assert = require('yeoman-assert');
var helpers = require('yeoman-test');
var fse = require('fs-extra');

const constants = require('../generators/generator-constants'),
    TEST_DIR = constants.TEST_DIR,
    CLIENT_MAIN_SRC_DIR = constants.CLIENT_MAIN_SRC_DIR,
    CLIENT_TEST_SRC_DIR = constants.CLIENT_TEST_SRC_DIR,
    SERVER_MAIN_SRC_DIR = constants.SERVER_MAIN_SRC_DIR,
    SERVER_TEST_SRC_DIR = constants.SERVER_TEST_SRC_DIR;

const expectedFiles = {
    client : [
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foos.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-detail.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-dialog.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-delete-dialog.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo.state.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-dialog.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-delete-dialog.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-detail.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo.service.js',
        CLIENT_TEST_SRC_DIR + 'spec/app/entities/foo/foo-detail.controller.spec.js',
    ],
    clientWithSuffix : [
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foos-management.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management-detail.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management-dialog.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management-delete-dialog.html',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management.state.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management-dialog.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management-delete-dialog.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo-management-detail.controller.js',
        CLIENT_MAIN_SRC_DIR + 'app/entities/foo/foo.service.js',
        CLIENT_TEST_SRC_DIR + 'spec/app/entities/foo/foo-management-detail.controller.spec.js',
    ],
    server : [
        '.jhipster/Foo.json',
        SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/domain/Foo.java',
        SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/repository/FooRepository.java',
        SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/web/rest/FooResource.java',
        // SERVER_MAIN_RES_DIR + 'config/liquibase/changelog/20160120213555_added_entity_Foo.xml',
        SERVER_TEST_SRC_DIR + 'com/mycompany/myapp/web/rest/FooResourceIntTest.java',
        TEST_DIR + 'gatling/simulations/FooGatlingTest.scala',
    ]
}
describe('JHipster generator entity', function () {
    describe('no dto, no service, no pagination', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'no',
                    service: 'no',
                    pagination: 'no'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
            assert.file([
                //CLIENT_MAIN_SRC_DIR + 'i18n/en/foo.json', //this should ideally be working
                //CLIENT_MAIN_SRC_DIR + 'i18n/fr/foo.json'
            ]);
        });
    });

    describe('with dto, no service, no pagination', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'mapstruct',
                    service: 'no',
                    pagination: 'no'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
            assert.file([
                SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/web/rest/dto/FooDTO.java',
                SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/web/rest/mapper/FooMapper.java'
            ]);
        });
    });

    describe('no dto, with serviceClass, no pagination', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'no',
                    service: 'serviceClass',
                    pagination: 'no'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
            assert.file([,
                SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/service/FooService.java'
            ]);
        });
    });

    describe('no dto, with serviceImpl, no pagination', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'no',
                    service: 'serviceImpl',
                    pagination: 'no'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
            assert.file([
                SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/service/FooService.java',
                SERVER_MAIN_SRC_DIR + 'com/mycompany/myapp/service/impl/FooServiceImpl.java'
            ]);
        });
    });

    describe('no dto, no service, with pager', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'no',
                    service: 'no',
                    pagination: 'pager'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
        });
    });

    describe('no dto, no service, with pagination', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'no',
                    service: 'no',
                    pagination: 'pagination'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
        });
    });

    describe('no dto, no service, with infinite-scroll', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'no',
                    service: 'no',
                    pagination: 'infinite-scroll'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
        });
    });

    describe('with dto, serviceImpl, with hazelcast, elasticsearch and noi18n', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/noi18n'), dir)
                })
                .withArguments(['foo'])
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'yes',
                    service: 'serviceImpl',
                    pagination: 'infinite-scroll'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.client);
            assert.noFile([
                CLIENT_MAIN_SRC_DIR + 'i18n/en/foo.json',
                CLIENT_MAIN_SRC_DIR + 'i18n/fr/foo.json'
            ])
        });
    });

    describe('with angulr suffix', function () {
        beforeEach(function (done) {
            helpers.run(require.resolve('../generators/entity'))
                .inTmpDir(function (dir) {
                    fse.copySync(path.join(__dirname, '../test/templates/default'), dir)
                })
                .withArguments(['foo'])
                .withOptions({'angular-suffix': 'management'})
                .withPrompts({
                    fieldAdd: false,
                    relationshipAdd: false,
                    dto: 'yes',
                    service: 'serviceImpl',
                    pagination: 'infinite-scroll'
                })
                .on('end', done);
        });

        it('creates expected default files', function () {
            assert.file(expectedFiles.server);
            assert.file(expectedFiles.clientWithSuffix);
            assert.fileContent('.jhipster/Foo.json', 'angularJSSuffix');
        });
    });
});
<|endoftext|>


<commit_before>var assert = chai.assert;

function child(node /*...*/) {
    for (var i = 1; i < arguments.length; i++) {
        node = node.childNodes[arguments[i]];
    }
    return node;
}

suite('events', function () {

    test('click event to dispatch', function (done) {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '<button onclick="clicked">click me</button>' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {};
        Magery.patch(templates, 'main', container, data);
        container.dispatch = function (name, event, context, path) {
            assert.equal(name, 'clicked');
            assert.equal(event.target, child(container, 0));
            assert.deepEqual(context, data);
            assert.deepEqual(path, []);
            done();
        };
        child(container, 0).click();
    });

    test('click event with nested context', function (done) {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '{{#with user}}' +
                    '<button onclick="clicked">click me</button>' +
                  '{{/with}}' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {user: {name: 'test'}};
        Magery.patch(templates, 'main', container, data);
        container.dispatch = function (name, event, context, path) {
            assert.equal(name, 'clicked');
            assert.equal(event.target, child(container, 0));
            assert.deepEqual(context, {name: 'test'});
            assert.deepEqual(path, ['user']);
            done();
        };
        child(container, 0).click();
    });

    test('text input value reset to match template data on input', function () {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '<input type="text" value="{{name}}">' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {name: 'testing'};
        Magery.patch(templates, 'main', container, data);
        var input = child(container, 0);
        input.value = 'foo';
        assert.equal(input.value, 'foo');
        // input event fires synchronously
        var event = document.createEvent('Event');
        event.initEvent('input', true, true);
        input.dispatchEvent(event);
        assert.equal(input.value, 'testing');
    });

    test('update input value via dispatch + patch', function () {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '<input type="text" value="{{name}}" oninput="updateInput">' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {name: 'testing'};
        Magery.patch(templates, 'main', container, data);
        container.dispatch = function (name, event, context, path) {
            if (name == 'updateInput') {
                data.name = 'bar';
                Magery.patch(templates, 'main', container, data);
            }
        };
        var input = child(container, 0);
        input.value = 'foo';
        assert.equal(input.value, 'foo');
        // input event fires synchronously
        var event = document.createEvent('Event');
        event.initEvent('input', true, true);
        input.dispatchEvent(event);
        assert.equal(input.value, 'bar');
    });

    // TODO: check this in other browsers and confirm it actually tests cursor position changes
    /*
    test('maintain text cursor position if patch results in expected event value', function () {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '<input type="text" value="{{name}}" oninput="updateInput">' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {name: 'testing'};
        Magery.patch(templates, 'main', container, data);
        container.dispatch = function (name, event, context, path) {
            if (name == 'updateInput') {
                data.name = event.target.value;
                Magery.patch(templates, 'main', container, data);
            }
        };
        var input = child(container, 0);
        document.body.appendChild(container);
        input.focus();
        input.value = 'test-ing';
        input.selectionStart = 5;
        input.selectionEnd = 5;
        // input event fires synchronously
        var event = document.createEvent('Event');
        event.initEvent('input', true, true);
        input.dispatchEvent(event);
        assert.equal(input.value, 'test-ing');
        assert.equal(input.selectionStart, 5);
        assert.equal(input.selectionEnd, 5);
        document.body.removeChild(container);
    });
     */

    test('reset checkbox to match template data', function (done) {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '{{#if checked}}' +
                    '<input type="checkbox" checked>' +
                  '{{else}}' +
                    '<input type="checkbox">' +
                  '{{/if}}' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {checked: true};
        Magery.patch(templates, 'main', container, data);
        var input = child(container, 0);
        document.body.appendChild(container);
        assert.ok(input.checked);
        input.click();
        setTimeout(function () {
            assert.ok(input.checked);
            document.body.removeChild(container);
            done();
        }, 0);
    });

    test('reset radio to match template data', function (done) {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '{{#each options}}' +
                    '{{#if checked}}' +
                      '<input type="radio" name="example" value="{{value}}" checked>' +
                    '{{else}}' +
                      '<input type="radio" name="example" value="{{value}}">' +
                    '{{/if}}' +
                  '{{/each}}' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {
            options: [
                {value: 'one', checked: false},
                {value: 'two', checked: true},
                {value: 'three', checked: false}
            ]
        };
        Magery.patch(templates, 'main', container, data);
        var radioOne = child(container, 0);
        var radioTwo = child(container, 1);
        var radioThree = child(container, 2);
        document.body.appendChild(container);
        assert.ok(!radioOne.checked, 'radio one (pre)');
        assert.ok(radioTwo.checked, 'radio two (pre)');
        assert.ok(!radioThree.checked, 'radio three (pre)');
        radioThree.click();
        setTimeout(function () {
            assert.ok(!radioOne.checked, 'radio one (post)');
            assert.ok(radioTwo.checked, 'radio two (post)');
            assert.ok(!radioThree.checked, 'radio three (post)');
            document.body.removeChild(container);
            done();
        }, 0);
    });

});
<commit_msg>Add test for select and options element events<commit_after>var assert = chai.assert;

function child(node /*...*/) {
    for (var i = 1; i < arguments.length; i++) {
        node = node.childNodes[arguments[i]];
    }
    return node;
}

suite('events', function () {

    test('click event to dispatch', function (done) {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '<button onclick="clicked">click me</button>' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {};
        Magery.patch(templates, 'main', container, data);
        container.dispatch = function (name, event, context, path) {
            assert.equal(name, 'clicked');
            assert.equal(event.target, child(container, 0));
            assert.deepEqual(context, data);
            assert.deepEqual(path, []);
            done();
        };
        child(container, 0).click();
    });

    test('click event with nested context', function (done) {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '{{#with user}}' +
                    '<button onclick="clicked">click me</button>' +
                  '{{/with}}' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {user: {name: 'test'}};
        Magery.patch(templates, 'main', container, data);
        container.dispatch = function (name, event, context, path) {
            assert.equal(name, 'clicked');
            assert.equal(event.target, child(container, 0));
            assert.deepEqual(context, {name: 'test'});
            assert.deepEqual(path, ['user']);
            done();
        };
        child(container, 0).click();
    });

    test('text input value reset to match template data on input', function () {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '<input type="text" value="{{name}}">' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {name: 'testing'};
        Magery.patch(templates, 'main', container, data);
        var input = child(container, 0);
        input.value = 'foo';
        assert.equal(input.value, 'foo');
        // input event fires synchronously
        var event = document.createEvent('Event');
        event.initEvent('input', true, true);
        input.dispatchEvent(event);
        assert.equal(input.value, 'testing');
    });

    test('update input value via dispatch + patch', function () {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '<input type="text" value="{{name}}" oninput="updateInput">' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {name: 'testing'};
        Magery.patch(templates, 'main', container, data);
        container.dispatch = function (name, event, context, path) {
            if (name == 'updateInput') {
                data.name = 'bar';
                Magery.patch(templates, 'main', container, data);
            }
        };
        var input = child(container, 0);
        input.value = 'foo';
        assert.equal(input.value, 'foo');
        // input event fires synchronously
        var event = document.createEvent('Event');
        event.initEvent('input', true, true);
        input.dispatchEvent(event);
        assert.equal(input.value, 'bar');
    });

    // TODO: check this in other browsers and confirm it actually tests cursor position changes
    /*
    test('maintain text cursor position if patch results in expected event value', function () {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '<input type="text" value="{{name}}" oninput="updateInput">' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {name: 'testing'};
        Magery.patch(templates, 'main', container, data);
        container.dispatch = function (name, event, context, path) {
            if (name == 'updateInput') {
                data.name = event.target.value;
                Magery.patch(templates, 'main', container, data);
            }
        };
        var input = child(container, 0);
        document.body.appendChild(container);
        input.focus();
        input.value = 'test-ing';
        input.selectionStart = 5;
        input.selectionEnd = 5;
        // input event fires synchronously
        var event = document.createEvent('Event');
        event.initEvent('input', true, true);
        input.dispatchEvent(event);
        assert.equal(input.value, 'test-ing');
        assert.equal(input.selectionStart, 5);
        assert.equal(input.selectionEnd, 5);
        document.body.removeChild(container);
    });
     */

    test('reset checkbox to match template data', function (done) {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '{{#if checked}}' +
                    '<input type="checkbox" checked>' +
                  '{{else}}' +
                    '<input type="checkbox">' +
                  '{{/if}}' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {checked: true};
        Magery.patch(templates, 'main', container, data);
        var input = child(container, 0);
        document.body.appendChild(container);
        assert.ok(input.checked);
        input.click();
        setTimeout(function () {
            assert.ok(input.checked);
            document.body.removeChild(container);
            done();
        }, 0);
    });

    test('reset radio to match template data', function (done) {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '{{#each options}}' +
                    '{{#if checked}}' +
                      '<input type="radio" name="example" value="{{value}}" checked>' +
                    '{{else}}' +
                      '<input type="radio" name="example" value="{{value}}">' +
                    '{{/if}}' +
                  '{{/each}}' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {
            options: [
                {value: 'one', checked: false},
                {value: 'two', checked: true},
                {value: 'three', checked: false}
            ]
        };
        Magery.patch(templates, 'main', container, data);
        var radioOne = child(container, 0);
        var radioTwo = child(container, 1);
        var radioThree = child(container, 2);
        document.body.appendChild(container);
        assert.ok(!radioOne.checked, 'radio one (pre)');
        assert.ok(radioTwo.checked, 'radio two (pre)');
        assert.ok(!radioThree.checked, 'radio three (pre)');
        radioThree.click();
        setTimeout(function () {
            assert.ok(!radioOne.checked, 'radio one (post)');
            assert.ok(radioTwo.checked, 'radio two (post)');
            assert.ok(!radioThree.checked, 'radio three (post)');
            document.body.removeChild(container);
            done();
        }, 0);
    });

    test('reset select to match template data', function (done) {
        var container = document.createElement('div');
        var src = '' +
                '{{#define main}}' +
                  '<select>' +
                    '{{#each options}}' +
                      '{{#if selected}}' +
                        '<option value="{{value}}" selected>{{label}}</option>' +
                      '{{else}}' +
                        '<option value="{{value}}">{{label}}</option>' +
                      '{{/if}}' +
                    '{{/each}}' +
                  '</select>' +
                '{{/define}}';
        var templates = Magery.loadTemplates(src);
        var data = {
            options: [
                {value: 1, label: 'one', selected: false},
                {value: 2, label: 'two', selected: true},
                {value: 3, label: 'three', selected: false}
            ]
        };
        Magery.patch(templates, 'main', container, data);
        var select = child(container, 0);
        var optionOne = child(select, 0);
        var optionTwo = child(select, 1);
        var optionThree = child(select, 2);
        document.body.appendChild(container);
        assert.ok(!optionOne.selected, 'option one (pre)');
        assert.ok(optionTwo.selected, 'option two (pre)');
        assert.ok(!optionThree.selected, 'option three (pre)');
        assert.equal(select.value, 2);
        optionThree.click();
        setTimeout(function () {
            assert.ok(!optionOne.selected, 'option one (pre)');
            assert.ok(optionTwo.selected, 'option two (pre)');
            assert.ok(!optionThree.selected, 'option three (pre)');
            assert.equal(select.value, 2);
            document.body.removeChild(container);
            done();
        }, 0);
    });

});
<|endoftext|>


<commit_before>PSB.Model = (function ($) {
	"use strict";

	var BudgetRoot = Parse.Object.extend("BudgetRoot");
	var BudgetNode = Parse.Object.extend("BudgetNode");
	var CostCenter = Parse.Object.extend("CostCenter");
	var FundingType = Parse.Object.extend("FundingType");

	function _handleParseError(errObj) {
		PSB.Controller.handleError('Parse Error ' + errObj.code + ': ' + errObj.message);
	}

	function _fetchChildNodes(parentNode, successCallback) {
		var query = new Parse.Query(BudgetNode);
		query.equalTo("parent", parentNode).find({
			success: successCallback,
			error: _handleParseError
		});
	}

	function _parseArrayToJson(objArray) {
		return objArray.map(function (obj) { return obj.toJSON(); });
	}

	return {
		init: function () {
			Parse.initialize(
				"kfsmOGmVzW3UKCb3S0gpMpq6aEs7M7AG7dum0Osy",
				"NVHRaFSafDWCnknX4ckxDavdvcfWqD9bmN2KiI1u");

			return this;
		},

		/*
		 * Fetch the root budget level from which all other categories descend.
		 */
		fetchBudgetRoot: function (successCallback) {
			var query = new Parse.Query(BudgetRoot);
			query.limit(1).first({
				success: function(rootNode) {
					_fetchChildNodes(rootNode, function (childNodes) {
						successCallback(rootNode.toJSON(), _parseArrayToJson(childNodes));
					});
				},
				error: _handleParseError
			});
		},

		/*
		 * Fetch the budget subcategories for the parent category specified by ID.
		 */
		fetchBudgetChildren: function (parentId, successCallback) {
			var parentNode = new BudgetNode();
			parentNode.id = parentId;
			_fetchChildNodes(parentNode, function (childNodes) {
				successCallBack(_parseArrayToJson(childNodes));
			});
		}
	};
}(jQuery));
<commit_msg>Handle case of no root node found.<commit_after>PSB.Model = (function ($) {
	"use strict";

	var BudgetRoot = Parse.Object.extend("BudgetRoot");
	var BudgetNode = Parse.Object.extend("BudgetNode");
	var CostCenter = Parse.Object.extend("CostCenter");
	var FundingType = Parse.Object.extend("FundingType");

	function _handleParseError(errObj) {
		PSB.Controller.handleError('Parse Error ' + errObj.code + ': ' + errObj.message);
	}

	function _fetchChildNodes(parentNode, successCallback) {
		var query = new Parse.Query(BudgetNode);
		query.equalTo("parent", parentNode).find({
			success: successCallback,
			error: _handleParseError
		});
	}

	function _parseArrayToJson(objArray) {
		return objArray.map(function (obj) { return obj.toJSON(); });
	}

	return {
		init: function () {
			Parse.initialize(
				"kfsmOGmVzW3UKCb3S0gpMpq6aEs7M7AG7dum0Osy",
				"NVHRaFSafDWCnknX4ckxDavdvcfWqD9bmN2KiI1u");

			return this;
		},

		/*
		 * Fetch the root budget level from which all other categories descend.
		 */
		fetchBudgetRoot: function (successCallback) {
			var query = new Parse.Query(BudgetRoot);
			query.limit(1).first({
				success: function(rootNode) {
					if (rootNode) {
						_fetchChildNodes(rootNode, function (childNodes) {
							successCallback(rootNode.toJSON(), _parseArrayToJson(childNodes));
						});
					} else {
						PSB.Controller.handleError('No budget root node found.');
					}
				},
				error: _handleParseError
			});
		},

		/*
		 * Fetch the budget subcategories for the parent category specified by ID.
		 */
		fetchBudgetChildren: function (parentId, successCallback) {
			var parentNode = new BudgetNode();
			parentNode.id = parentId;
			_fetchChildNodes(parentNode, function (childNodes) {
				successCallBack(_parseArrayToJson(childNodes));
			});
		}
	};
}(jQuery));
<|endoftext|>


<commit_before>import Ember from 'ember';
import Base from 'ember-simple-auth/authenticators/base';

const { RSVP, $, isEmpty, run } = Ember;

export default Base.extend({
  restore(data) {
    console.warn('restore called: ', data);
    return new RSVP.Promise((resolve, reject) => {
      if (isEmpty(data.username) || isEmpty(data.password) || isEmpty(data.protocol) || isEmpty(data.subdomain)) {
        reject();
      }
      else {
        resolve(data);
      }
    });
  },

  authenticate(protocol, subdomain, username, password) {
    return new RSVP.Promise((resolve, reject) => {
      this.callLogin(protocol, subdomain, username, password).then((response) => {
        run(() => {
          resolve({
            protocol,
            subdomain,
            username,
            password,
            id: response['id'],
            domainTitle: response['title'],
            accessKey: response['access_key'],
            host: `${protocol}://${subdomain}.unfuddle.com`
          });
        });
      }, (/*xhr*/) => {
        run(null, reject, 'Could not authenticate against Unfuddle, are your user details correct?');
      });
    });
  },

  // @TODO
  // invalidate(data) {
  // },

  callLogin(protocol, subdomain, username, password) {
    let url = `${protocol}://${subdomain}.unfuddle.com/api/v1/account`;
    let options = {
      url,
      type: 'GET',
      dataType: 'json',
      headers: {
        Accept: 'application/json',
        Authorization: `Basic ` + btoa(`${username}:${password}`)
      }
    };

    return $.ajax(options);
  }
});
<commit_msg>Fixed JSCS issues.<commit_after>import Ember from 'ember';
import Base from 'ember-simple-auth/authenticators/base';

const { RSVP, $, isEmpty, run } = Ember;

export default Base.extend({
  restore(data) {
    return new RSVP.Promise((resolve, reject) => {
      if (isEmpty(data.username) || isEmpty(data.password) || isEmpty(data.protocol) || isEmpty(data.subdomain)) {
        reject();
      } else {
        resolve(data);
      }
    });
  },

  authenticate(protocol, subdomain, username, password) {
    return new RSVP.Promise((resolve, reject) => {
      this.callLogin(protocol, subdomain, username, password).then((response) => {
        run(() => {
          resolve({
            protocol,
            subdomain,
            username,
            password,
            id: response.id,
            domainTitle: response.title,
            accessKey: response['access_key'], // jscs:disable requireDotNotation
            host: `${protocol}://${subdomain}.unfuddle.com`
          });
        });
      }, (/*xhr*/) => {
        run(null, reject, 'Could not authenticate against Unfuddle, are your user details correct?');
      });
    });
  },

  // @TODO
  // invalidate(data) {
  // },

  callLogin(protocol, subdomain, username, password) {
    let url = `${protocol}://${subdomain}.unfuddle.com/api/v1/account`;
    let options = {
      url,
      type: 'GET',
      dataType: 'json',
      headers: {
        Accept: 'application/json',
        Authorization: `Basic ` + btoa(`${username}:${password}`)
      }
    };

    return $.ajax(options);
  }
});
<|endoftext|>


<commit_before>var Model = function () {

	this.getDataOnline = function(input) {
	// Get data and transform
		result = false;
		$.ajax({
			async: false,
			crossDomain: true,
			url: input
		})
			.done(function (data){
				if (!data) {
					log('getData error failed but completed, data = null: ' + url);
				} else {
					log(data);
					if (typeof data === "object" && data !== null) {
						log('object returned');
						result = data;
					} else {
						log('JSON returned');
						try {
							result = $.parseJSON(data);
						} catch (err) {
							log('getData Json parse error: ' + err);
							result = false;
						}
					}
				}
			})
			.error(function (request, status, error){
				log('getData error failed : ' + input + ", " + status + ", " + error);
			});
		return result;
	},

	this.getImage = function(url, callback, output) {
	    var canvas = document.createElement('CANVAS');
	    ctx = canvas.getContext('2d');
	    img = new Image;
	   	img.crossOrigin = 'Anonymous';
	    img.onload = function(){
	        var dataURL;
	        canvas.height = img.height;
	        canvas.width = img.width;
	        ctx.drawImage(img, 0, 0);
	        dataURL = canvas.toDataURL(output);
	        canvas = null;
	        //log(dataURL);
	        callback.call(this, dataURL);
	    };
	    img.src = url;
	},

	//Requirement: questions must be fetched at least once on first boot
	this.getData = function (input, force){
		var auth0_request = (input.indexOf('quackr.auth0.com') > -1);
		if (!auth0_request){
		    var ttl = this.getLocal('TTL_' + input.trim());
			log('TTL is ' + ttl + ' for ' + input);
			now = new Date();
			now.setDate(now.getDate());
			log('(' + ttl + ' vs ' + now.getTime() + ')');
		}
        if (auth0_request || (!ttl) || (isNaN(ttl)) || ttl <= now.getTime() || (force)){
			log('Auth0 request or TTL expired/not existant. Fetching online..');
			//if it doesnt exist cached or TTL is more than a day old
			var result = this.getDataOnline(input);
			//result = this.convertAPIdata(result);
			if (auth0_request){
				return result;
			} else {
				if (result){
					log('Putting result in cache..');
					// Fill our local database
					this.putLocal(input.trim(), result);
					// Adjust/add the TTL
					now.setTime(now.getTime() + 86400000); //one extra day
					this.putLocal('TTL_' + input.trim(), now.getTime());
					log('Put in cache:');
					log(this.getLocal(input.trim()));
					return result;
				} else {
					if (ttl){
						log('No network, but we have a cached version.');
						//Online doesnt work, but we have a local copy!
						return this.getLocal(input.trim());
					} else {
						log('No network and no cached version.. Returning false..');
						//Online doesnt work and we dont have a local copy..
						return false;
					}
				}
			}
		} else {
			log('TTL valid, opening from cache..');
			var local = this.getLocal(input.trim());
			log(local);
			if (local){
				//Safe to use our cached copy
				return local;
			} else {
				//Local db is non existant. Try to fetch online by removing TTL and recursive call.
				log('Local db is empty. Removing TTL and recursive calling myself.');
				this.removeLocal('TTL_' + input.trim());
				return this.getData(input);
			}
		}
	},

	this.getMoreQuestions = function () {
		var categories = this.getCategories();
		var r = false;
		categories.forEach(function (entry){
			//Will be cached automagically.
			log('Getting questions for category ' + entry.id);
			var re = (app.model.getQuestions(entry.id, true));
			if (re){
				r = true;
			}
		});
		return r;
	},

	this.submit = function (url, data) {
		//Handle everything in once
		this.storeSubmit(url, data);
		this.doSubmits();
	}

	this.dosubmit = function(url, data) {
		log('Submitting:');
		log(data);
		$.post( url, JSON.stringify(data), "json")
			.done( function () {
			  	log('submit successful!');
			    log('Submitting our queue..');
			    //if a submit is successful, let it be.
			    this.doSubmits();
			})
			.fail( function (){
				log('submit failed for ' + url + '. Adding to local queue.');
				//if a submit failed, re-add it locally
				this.storeSubmit(url, data);
			});
	},

	this.doSubmits = function () {
		//This could mean wrong submits if the user cancels/exits during this process. Only for a day, so meh.
		log('Processing submits..');
		var submits = this.getSubmits();
		this.removeLocal('submits'); //only re-add when failed
		submits.forEach( function (entry) {
			dosubmit(entry.url, entry.data);
		});
	},

	this.storeSubmit = function (url, data){
		log('Storing submit for ' + url);2
		var submits = this.getLocal('submits');
		if (!submits){
			submits = [];
		}
		submits.push([url, data]);
		this.putLocal('submits', submits);
	},

	this.getSubmits = function () {
		return this.getLocal('submits');
	},

    this.deleteProgress = function(){
        var deferration = $.Deferred();
        $.ajax({
            url: this.userURL + 'reset',
            type: 'POST',
            cache: true,
            success: function (data) {
                log('delete result:' +data);
            },
            complete: function (){
                deferration.resolve();
            },
        });
        return deferration;
    },

	this.getQuestions = function(catid, force) {
		//GET secured/category/{id}(/random)
		var r = this.getData(this.categoryURL + catid + '/random/' + '20', force);
		log('getQuestions for category ' + catid);
		log(r);
		return r;
	},

	this.getRandomQuestion = function(catid) {
		//return this.getData(this.categoryURL + catid + '/random');
		var all = this.getQuestions(catid);
		if (all && all.questions && all.questions.length > 0){
			var r = all.questions[Math.floor(Math.random()*all.questions.length)];
			log('Random question;');
			log(r);
		} else {
			log('no random questions available');
			log(all);
			r = false;
		}
		return r[0];
	},

	this.getCategories = function() {
		return this.getData(this.categoriesURL);
	},

	this.getProfile = function() {
		var profile = this.getData(this.profileURL);
		this.getImage(profile.picture, function(base64Img){
			profile.picture = base64Img;
			log('Set profile.picture to ' + profile.picture);
		});

		return profile;
	},

	this.getCategoryAnswered = function(catid) {
		return this.getData(this.userURL + catid + '/progress');
		//return this.getQuestions(catid); //everything is removed when it is answered
	},

	this.getRandomQuestions = function(catid, count) {
		//return this.getData(this.categoryURL + catid + '/random/' + count);
		var result = [];
		var all = this.getQuestions(catid);
		if (all.questions.length > 0){
			for (i = 0; i < count && i <= all.questions.length; i++) {
				var item = all.questions[Math.floor(Math.random()*all.questions.length)];
				all.questions.splice(all.questions.indexOf(item), 1);
				result.push(item);
			}
		}
		log('random questions:');
		log(result);
		return result;
	},

	this.setupURLs = function () {
		this.baseURL = 'http://d00med.net/quackr/';
		this.secURL = this.baseURL + 'secured/';
		this.profileURL = 'https://quackr.auth0.com/api/users/' + localStorage.getItem('userID');

		this.categoriesURL = this.secURL + 'categories';
		this.categoryURL   = this.secURL + 'category/';
		this.questionURL   = this.secURL + 'question/';
		this.userURL 	   = this.secURL + 'user/';

		this.submitURL	   = this.userURL + 'submit';
	},

	this.sendNumbers = function() {
		var solved_arr = this.getLocal('solved');
		var wrong_arr  = this.getLocal('wrong');
		if (solved_arr || wrong_arr){
			log('Submitting to server..');
			error = false;
			try {
				data = {};
				// Question ids of wrong answers
				data['wrong'] = [];
				wrong_arr.forEach(function (entry){
					data['wrong'].push(entry);
				});
				// Question ids of correct answers
				data['solved'] = [];
				solved_arr.forEach(function (entry){
					data['solved'].push(entry);
				});
				this.submit(this.submitURL, data);
				log('Submitted progress.');
				log(data);
				//flush cache, because the server is updated
				this.getMoreQuestions();
			} catch (err) {
				log('Submit failed; ' + err);
				error = true;
			}
			return error;
		} else {
			log('Nothing to submit');
			return true;
		}
	},

	this.removeQuestionFromCache = function (questionid) {
		var all = this.getCategories();
		var done = false;
		new_arr = [];
		all.forEach(function(cat){
			if (!done){
				var cat_id = cat.id;
				var all_question = app.model.getQuestions(cat_id);
				if (all_question){
					all_question.questions.forEach(function(question){
						var question_id = question.id;
						if (question.id != questionid){
							new_arr.push(question);
						} else {
							log('question ' + questionid + ' found!');
							//found the category
							done = true;
						}
					});
					if (done){
						//this is the category you're looking for. Replace cached questions with a version without the question
						log('New cached version:');
						log(new_arr);
						this.putLocal(this.categoryURL + cat.id, new_arr);
					}
				}
			}
		});
		return done;
	},

	this.correct = function (questionid) {
		//remove question from cache
		log('Removing question id ' + questionid + ' from cache.');
		//update numbers
		//http://d00med.net/quackr/secured/user/1/progress
		this.removeQuestionFromCache(questionid);

		var solved_arr = this.getLocal('solved');
		if (solved_arr){
			solved_arr.push(questionid);
			if (this.sendNumbers()){
				this.removeLocal('solved');
			} else {
				this.putLocal('solved', solved_arr);
			}
		} else {
			this.putLocal('solved', [ questionid ]);
		}
	},

	this.incorrect = function (questionid) {
		//update numbers
		//http://d00med.net/quackr/secured/user/1/progress

		var wrong = this.getLocal('wrong');
		if (wrong){
			wrong.push(questionid);
			if (this.sendNumbers()){
				this.removeLocal('wrong');
			} else {
				this.putLocal('wrong', wrong);
			}
		} else {
			this.putLocal('wrong', [ questionid ]);
		}
	},

	this.removeLocal = function (key) {
		return $.jStorage.deleteKey(key);
	},

	this.getLocal = function (key) {
		return $.jStorage.get(key, false);
	},

	this.putLocal = function (key, value) {
		return $.jStorage.set(key, value);
	},

	this.initialize = function () {
	// Model constructor
		//all online URLs
		this.setupURLs();

		//do we need to work offline?
		this.online = (navigator.onLine);
		log('Network status: ' + this.online);

		//is this our first run?
		if (!this.getLocal('first')){
			//redirect('we_need_internet');
			//TODO: show page until internet connection is reached
			//TODO: fetchFirstLevels(all_categories)
		}
	},

	this.initialize();
};
<commit_msg>USe proper date api<commit_after>var Model = function () {

	this.getDataOnline = function(input) {
	// Get data and transform
		result = false;
		$.ajax({
			async: false,
			crossDomain: true,
			url: input
		})
			.done(function (data){
				if (!data) {
					log('getData error failed but completed, data = null: ' + url);
				} else {
					log(data);
					if (typeof data === "object" && data !== null) {
						log('object returned');
						result = data;
					} else {
						log('JSON returned');
						try {
							result = $.parseJSON(data);
						} catch (err) {
							log('getData Json parse error: ' + err);
							result = false;
						}
					}
				}
			})
			.error(function (request, status, error){
				log('getData error failed : ' + input + ", " + status + ", " + error);
			});
		return result;
	},

	this.getImage = function(url, callback, output) {
	    var canvas = document.createElement('CANVAS');
	    ctx = canvas.getContext('2d');
	    img = new Image;
	   	img.crossOrigin = 'Anonymous';
	    img.onload = function(){
	        var dataURL;
	        canvas.height = img.height;
	        canvas.width = img.width;
	        ctx.drawImage(img, 0, 0);
	        dataURL = canvas.toDataURL(output);
	        canvas = null;
	        //log(dataURL);
	        callback.call(this, dataURL);
	    };
	    img.src = url;
	},

	//Requirement: questions must be fetched at least once on first boot
	this.getData = function (input, force){
		var auth0_request = (input.indexOf('quackr.auth0.com') > -1);
		if (!auth0_request){
		    var ttl = this.getLocal('TTL_' + input.trim());
			log('TTL is ' + ttl + ' for ' + input);
			now = new Date();
			now.setDate(now.getDate());
			log('(' + ttl + ' vs ' + now.getTime() + ')');
		}
        if (auth0_request || (!ttl) || (isNaN(ttl)) || ttl <= now.getTime() || (force)){
			log('Auth0 request or TTL expired/not existant. Fetching online..');
			//if it doesnt exist cached or TTL is more than a day old
			var result = this.getDataOnline(input);
			//result = this.convertAPIdata(result);
			if (auth0_request){
				return result;
			} else {
				if (result){
					log('Putting result in cache..');
					// Fill our local database
					this.putLocal(input.trim(), result);
					// Adjust/add the TTL
					now.setTime(now.getTime() + 1); //one extra day
					this.putLocal('TTL_' + input.trim(), now.getTime());
					log('Put in cache:');
					log(this.getLocal(input.trim()));
					return result;
				} else {
					if (ttl){
						log('No network, but we have a cached version.');
						//Online doesnt work, but we have a local copy!
						return this.getLocal(input.trim());
					} else {
						log('No network and no cached version.. Returning false..');
						//Online doesnt work and we dont have a local copy..
						return false;
					}
				}
			}
		} else {
			log('TTL valid, opening from cache..');
			var local = this.getLocal(input.trim());
			log(local);
			if (local){
				//Safe to use our cached copy
				return local;
			} else {
				//Local db is non existant. Try to fetch online by removing TTL and recursive call.
				log('Local db is empty. Removing TTL and recursive calling myself.');
				this.removeLocal('TTL_' + input.trim());
				return this.getData(input);
			}
		}
	},

	this.getMoreQuestions = function () {
		var categories = this.getCategories();
		var r = false;
		categories.forEach(function (entry){
			//Will be cached automagically.
			log('Getting questions for category ' + entry.id);
			var re = (app.model.getQuestions(entry.id, true));
			if (re){
				r = true;
			}
		});
		return r;
	},

	this.submit = function (url, data) {
		//Handle everything in once
		this.storeSubmit(url, data);
		this.doSubmits();
	}

	this.dosubmit = function(url, data) {
		log('Submitting:');
		log(data);
		$.post( url, JSON.stringify(data), "json")
			.done( function () {
			  	log('submit successful!');
			    log('Submitting our queue..');
			    //if a submit is successful, let it be.
			    this.doSubmits();
			})
			.fail( function (){
				log('submit failed for ' + url + '. Adding to local queue.');
				//if a submit failed, re-add it locally
				this.storeSubmit(url, data);
			});
	},

	this.doSubmits = function () {
		//This could mean wrong submits if the user cancels/exits during this process. Only for a day, so meh.
		log('Processing submits..');
		var submits = this.getSubmits();
		this.removeLocal('submits'); //only re-add when failed
		submits.forEach( function (entry) {
			dosubmit(entry.url, entry.data);
		});
	},

	this.storeSubmit = function (url, data){
		log('Storing submit for ' + url);2
		var submits = this.getLocal('submits');
		if (!submits){
			submits = [];
		}
		submits.push([url, data]);
		this.putLocal('submits', submits);
	},

	this.getSubmits = function () {
		return this.getLocal('submits');
	},

    this.deleteProgress = function(){
        var deferration = $.Deferred();
        $.ajax({
            url: this.userURL + 'reset',
            type: 'POST',
            cache: true,
            success: function (data) {
                log('delete result:' +data);
            },
            complete: function (){
                deferration.resolve();
            },
        });
        return deferration;
    },

	this.getQuestions = function(catid, force) {
		//GET secured/category/{id}(/random)
		var r = this.getData(this.categoryURL + catid + '/random/' + '20', force);
		log('getQuestions for category ' + catid);
		log(r);
		return r;
	},

	this.getRandomQuestion = function(catid) {
		//return this.getData(this.categoryURL + catid + '/random');
		var all = this.getQuestions(catid);
		if (all && all.questions && all.questions.length > 0){
			var r = all.questions[Math.floor(Math.random()*all.questions.length)];
			log('Random question;');
			log(r);
		} else {
			log('no random questions available');
			log(all);
			r = false;
		}
		return r[0];
	},

	this.getCategories = function() {
		return this.getData(this.categoriesURL);
	},

	this.getProfile = function() {
		var profile = this.getData(this.profileURL);
		this.getImage(profile.picture, function(base64Img){
			profile.picture = base64Img;
			log('Set profile.picture to ' + profile.picture);
		});

		return profile;
	},

	this.getCategoryAnswered = function(catid) {
		return this.getData(this.userURL + catid + '/progress');
		//return this.getQuestions(catid); //everything is removed when it is answered
	},

	this.getRandomQuestions = function(catid, count) {
		//return this.getData(this.categoryURL + catid + '/random/' + count);
		var result = [];
		var all = this.getQuestions(catid);
		if (all.questions.length > 0){
			for (i = 0; i < count && i <= all.questions.length; i++) {
				var item = all.questions[Math.floor(Math.random()*all.questions.length)];
				all.questions.splice(all.questions.indexOf(item), 1);
				result.push(item);
			}
		}
		log('random questions:');
		log(result);
		return result;
	},

	this.setupURLs = function () {
		this.baseURL = 'http://d00med.net/quackr/';
		this.secURL = this.baseURL + 'secured/';
		this.profileURL = 'https://quackr.auth0.com/api/users/' + localStorage.getItem('userID');

		this.categoriesURL = this.secURL + 'categories';
		this.categoryURL   = this.secURL + 'category/';
		this.questionURL   = this.secURL + 'question/';
		this.userURL 	   = this.secURL + 'user/';

		this.submitURL	   = this.userURL + 'submit';
	},

	this.sendNumbers = function() {
		var solved_arr = this.getLocal('solved');
		var wrong_arr  = this.getLocal('wrong');
		if (solved_arr || wrong_arr){
			log('Submitting to server..');
			error = false;
			try {
				data = {};
				// Question ids of wrong answers
				data['wrong'] = [];
				wrong_arr.forEach(function (entry){
					data['wrong'].push(entry);
				});
				// Question ids of correct answers
				data['solved'] = [];
				solved_arr.forEach(function (entry){
					data['solved'].push(entry);
				});
				this.submit(this.submitURL, data);
				log('Submitted progress.');
				log(data);
				//flush cache, because the server is updated
				this.getMoreQuestions();
			} catch (err) {
				log('Submit failed; ' + err);
				error = true;
			}
			return error;
		} else {
			log('Nothing to submit');
			return true;
		}
	},

	this.removeQuestionFromCache = function (questionid) {
		var all = this.getCategories();
		var done = false;
		new_arr = [];
		all.forEach(function(cat){
			if (!done){
				var cat_id = cat.id;
				var all_question = app.model.getQuestions(cat_id);
				if (all_question){
					all_question.questions.forEach(function(question){
						var question_id = question.id;
						if (question.id != questionid){
							new_arr.push(question);
						} else {
							log('question ' + questionid + ' found!');
							//found the category
							done = true;
						}
					});
					if (done){
						//this is the category you're looking for. Replace cached questions with a version without the question
						log('New cached version:');
						log(new_arr);
						this.putLocal(this.categoryURL + cat.id, new_arr);
					}
				}
			}
		});
		return done;
	},

	this.correct = function (questionid) {
		//remove question from cache
		log('Removing question id ' + questionid + ' from cache.');
		//update numbers
		//http://d00med.net/quackr/secured/user/1/progress
		this.removeQuestionFromCache(questionid);

		var solved_arr = this.getLocal('solved');
		if (solved_arr){
			solved_arr.push(questionid);
			if (this.sendNumbers()){
				this.removeLocal('solved');
			} else {
				this.putLocal('solved', solved_arr);
			}
		} else {
			this.putLocal('solved', [ questionid ]);
		}
	},

	this.incorrect = function (questionid) {
		//update numbers
		//http://d00med.net/quackr/secured/user/1/progress

		var wrong = this.getLocal('wrong');
		if (wrong){
			wrong.push(questionid);
			if (this.sendNumbers()){
				this.removeLocal('wrong');
			} else {
				this.putLocal('wrong', wrong);
			}
		} else {
			this.putLocal('wrong', [ questionid ]);
		}
	},

	this.removeLocal = function (key) {
		return $.jStorage.deleteKey(key);
	},

	this.getLocal = function (key) {
		return $.jStorage.get(key, false);
	},

	this.putLocal = function (key, value) {
		return $.jStorage.set(key, value);
	},

	this.initialize = function () {
	// Model constructor
		//all online URLs
		this.setupURLs();

		//do we need to work offline?
		this.online = (navigator.onLine);
		log('Network status: ' + this.online);

		//is this our first run?
		if (!this.getLocal('first')){
			//redirect('we_need_internet');
			//TODO: show page until internet connection is reached
			//TODO: fetchFirstLevels(all_categories)
		}
	},

	this.initialize();
};
<|endoftext|>


<commit_before>(function($window) {
  'use strict';
  var blockDetailPage = angular.module('myApp.blockDetailPage', ['ngRoute', 'blockExplorerServices']);

  blockDetailPage.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/Block/:blockHash', {
      templateUrl: 'blockDetail/blockDetail.html',
      controller: 'BlockDetailPageCtrl',
    });
  }]);

  blockDetailPage.controller('BlockDetailPageCtrl', ['$scope', '$routeParams', 'BlockService', function($scope, $routeParams, BlockService) {
    var self = this;

    self.rootHash = '8dd171d6f04ba0f5df0c7d0491ae8455134c70ebdedc798bb4c9441d5ee03158';

    BlockService.getBlock($routeParams.blockHash).then(function(block) {
      $scope.$apply(function() {
        self.block = block;
      });
    });
  }]);

  blockDetailPage.directive('transactionBrowser', function() {
    return {
      templateUrl: 'blockDetail/transactionBrowser.html',
      restrict: 'E',
      scope: {
        transactions: '=transactions',
        visualisationHash: '=visualisationHash'
      },
      controller: function($scope) {
        var vm = this;

        $scope.updateHash = function (newHash) {
          $scope.visualisationHash = newHash;
        };

        $scope.$watch(function() {
            return $scope.transactions;
          }, function() {

            if (!$scope.transactions) {
              return;
            }

            vm.transactions = $scope.transactions;

            if (vm.transactions.length === 0) {
             return;
            }

            vm.currentIndex = 0;
            vm.currentTransactionsFromIndex(vm.currentIndex, 10);
          });

          vm.currentTransactionsFromIndex = function (index, number) {
            vm.currentTransactions = vm.transactions.slice(index, index+number);
          };

          vm.previousTen = function () {
            if (vm.currentIndex < 10) {
              return;
            }

            vm.currentIndex -= 10;

            vm.currentTransactionsFromIndex(vm.currentIndex, 10);
          };

          vm.nextTen = function () {
            if (vm.currentIndex > vm.transactions.length-10) {
              return;
            }

            vm.currentIndex += 10;

            vm.currentTransactionsFromIndex(vm.currentIndex, 10);
          };

          vm.remainingTransactions = function() {
            if (!vm.transactions) {
              return;
            }

            return Math.min(vm.transactions.length, vm.currentIndex + 10);
          };
      },
      controllerAs: 'controller',
    };
  });

  blockDetailPage.directive('visualisation', ['d3Service', 'BlockService', function(d3Service, BlockService) {
    return {
      restrict: 'EA',
      scope: {
        rootHash: "=rootHash",
      },
      link: function(scope, element, attrs) {

        var nodes = [], links = [], svg, force, height = 480, width = 640;

        d3Service.then(function(d3) {

          force = d3.layout.force();
          force.nodes(nodes);
          force.links(links);

          svg = d3.select('svg')
              .attr('width', width)
              .attr('height', height)
              .style('background-color', 'gray');

              svg.append("g").attr("class", "tx-links");
              svg.append("g").attr("class", "tx-nodes");

              update();
        });

        var update = function () {
          if (!svg || !force) return;

          var link = svg.selectAll('g.tx-links').selectAll('.link').data(links);

          link.enter().append('line')
            .attr('class', 'link');

          link.exit().remove();

          var node = svg.selectAll('g.tx-nodes').selectAll('.node, .anchor').data(nodes);

          node.enter().append('circle')
            .attr('class', 'node');

          node.exit().remove();

          force.on('tick', function() {

              console.log('layout ended');

              node.attr('r', 10)
                  .attr('cx', function(d) { return d.x; })
                  .attr('cy', function(d) { return d.y; });

              link.attr('x1', function(d) { return d.source.x; })
                  .attr('y1', function(d) { return d.source.y; })
                  .attr('x2', function(d) { return d.target.x; })
                  .attr('y2', function(d) { return d.target.y; });

          });

          force
          .size([width, height])
          .nodes(nodes)
          .links(links)
          .linkDistance(function(d) {
            return 5 / (1/d.layer);
          })
          .charge(-700)
          .chargeDistance(100)
          .alpha(2);

          force.start();
        };

        scope.$watch(function() {
          return scope.rootHash;
        }, function() {
          BlockService.getTransactions(scope.rootHash, 3)
          .then(function (result) {

            var dataFromTree = function () {
              var index = 0;

              return function (parent, layer) {
                parent.uid = index;
                nodes.push(parent);

                if (parent.children.length === 0) {
                  return;
                }

                parent.children.forEach(function(element) {
                  index++;

                  links.push({  source : parent,
                                target : element,
                                layer : layer});

                  dataFromTree(element, layer+1);
                });
              };
            }();

            nodes.length = 0;
            links.length = 0;
            dataFromTree(result[0], 0);

            nodes.slice().splice(0, 1).forEach(function (element, index) {
              if (index === 0) {
                element.fixed = true;
                element.x = width/2;
                element.y = height/2;
              } else {
                element.y = 0;
                element.x = 0;
              }
            });

            update();

          });

        });
      },
    };
  }]);

})(this);
<commit_msg>Don't render visualisation when d3 first loads.<commit_after>(function($window) {
  'use strict';
  var blockDetailPage = angular.module('myApp.blockDetailPage', ['ngRoute', 'blockExplorerServices']);

  blockDetailPage.config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/Block/:blockHash', {
      templateUrl: 'blockDetail/blockDetail.html',
      controller: 'BlockDetailPageCtrl',
    });
  }]);

  blockDetailPage.controller('BlockDetailPageCtrl', ['$scope', '$routeParams', 'BlockService', function($scope, $routeParams, BlockService) {
    var self = this;

    self.rootHash = '8dd171d6f04ba0f5df0c7d0491ae8455134c70ebdedc798bb4c9441d5ee03158';

    BlockService.getBlock($routeParams.blockHash).then(function(block) {
      $scope.$apply(function() {
        self.block = block;
      });
    });
  }]);

  blockDetailPage.directive('transactionBrowser', function() {
    return {
      templateUrl: 'blockDetail/transactionBrowser.html',
      restrict: 'E',
      scope: {
        transactions: '=transactions',
        visualisationHash: '=visualisationHash'
      },
      controller: function($scope) {
        var vm = this;

        $scope.updateHash = function (newHash) {
          $scope.visualisationHash = newHash;
        };

        $scope.$watch(function() {
            return $scope.transactions;
          }, function() {

            if (!$scope.transactions) {
              return;
            }

            vm.transactions = $scope.transactions;

            if (vm.transactions.length === 0) {
             return;
            }

            vm.currentIndex = 0;
            vm.currentTransactionsFromIndex(vm.currentIndex, 10);
          });

          vm.currentTransactionsFromIndex = function (index, number) {
            vm.currentTransactions = vm.transactions.slice(index, index+number);
          };

          vm.previousTen = function () {
            if (vm.currentIndex < 10) {
              return;
            }

            vm.currentIndex -= 10;

            vm.currentTransactionsFromIndex(vm.currentIndex, 10);
          };

          vm.nextTen = function () {
            if (vm.currentIndex > vm.transactions.length-10) {
              return;
            }

            vm.currentIndex += 10;

            vm.currentTransactionsFromIndex(vm.currentIndex, 10);
          };

          vm.remainingTransactions = function() {
            if (!vm.transactions) {
              return;
            }

            return Math.min(vm.transactions.length, vm.currentIndex + 10);
          };
      },
      controllerAs: 'controller',
    };
  });

  blockDetailPage.directive('visualisation', ['d3Service', 'BlockService', function(d3Service, BlockService) {
    return {
      restrict: 'EA',
      scope: {
        rootHash: "=rootHash",
      },
      link: function(scope, element, attrs) {

        var nodes = [], links = [], svg, force, height = 480, width = 640;

        d3Service.then(function(d3) {

          force = d3.layout.force();
          force.nodes(nodes);
          force.links(links);

          svg = d3.select('svg')
              .attr('width', width)
              .attr('height', height)
              .style('background-color', 'gray');

              svg.append("g").attr("class", "tx-links");
              svg.append("g").attr("class", "tx-nodes");
        });

        var update = function () {
          if (!svg || !force) return;

          var link = svg.selectAll('g.tx-links').selectAll('.link').data(links);

          link.enter().append('line')
            .attr('class', 'link');

          link.exit().remove();

          var node = svg.selectAll('g.tx-nodes').selectAll('.node, .anchor').data(nodes);

          node.enter().append('circle')
            .attr('class', 'node');

          node.exit().remove();

          force.on('tick', function() {

              console.log('layout ended');

              node.attr('r', 10)
                  .attr('cx', function(d) { return d.x; })
                  .attr('cy', function(d) { return d.y; });

              link.attr('x1', function(d) { return d.source.x; })
                  .attr('y1', function(d) { return d.source.y; })
                  .attr('x2', function(d) { return d.target.x; })
                  .attr('y2', function(d) { return d.target.y; });

          });

          force
          .size([width, height])
          .nodes(nodes)
          .links(links)
          .linkDistance(function(d) {
            return 5 / (1/d.layer);
          })
          .charge(-700)
          .chargeDistance(100)
          .alpha(2);

          force.start();
        };

        scope.$watch(function() {
          return scope.rootHash;
        }, function() {
          BlockService.getTransactions(scope.rootHash, 3)
          .then(function (result) {

            var dataFromTree = function () {
              var index = 0;

              return function (parent, layer) {
                parent.uid = index;
                nodes.push(parent);

                if (parent.children.length === 0) {
                  return;
                }

                parent.children.forEach(function(element) {
                  index++;

                  links.push({  source : parent,
                                target : element,
                                layer : layer});

                  dataFromTree(element, layer+1);
                });
              };
            }();

            nodes.length = 0;
            links.length = 0;
            dataFromTree(result[0], 0);

            nodes.slice().splice(0, 1).forEach(function (element, index) {
              if (index === 0) {
                element.fixed = true;
                element.x = width/2;
                element.y = height/2;
              } else {
                element.y = 0;
                element.x = 0;
              }
            });

            update();

          });

        });
      },
    };
  }]);

})(this);
<|endoftext|>


<commit_before>
var selectedLines = new Array();
function updateHighlightedLine() {
	updateHighlightedLineTo(window.location.hash);
}

function updateHighlightedLineTo(theNewHash) {

	for (var next in selectedLines) {
		document.getElementById('line' + selectedLines[next]).className = '';
		document.getElementById('anchor' + selectedLines[next]).className = 'lineAnchor';
	}
	selectedLines = new Array();

	var line = -1;
	if (theNewHash && theNewHash.match('L[0-9]+-L[0-9]+')) {
		var dashIndex = theNewHash.indexOf('-');
		var start = parseInt(theNewHash.substring(2, dashIndex));
		var end = parseInt(theNewHash.substring(dashIndex+2));
		for (var i = start; i <= end; i++) {
			selectedLines.push(i);
		}
	} else if (theNewHash && theNewHash.match('L[0-9]+')) {
		var line = parseInt(theNewHash.substring(2));
		selectedLines.push(line);
	}

	for (var next in selectedLines) {
		// Prevent us from scrolling to the selected line
		document.getElementById('L' + selectedLines[next]).name = '';
		// Select the line number column
		document.getElementById('line' + selectedLines[next]).className = 'selectedLine';
		// Select the response body column
		document.getElementById('anchor' + selectedLines[next]).className = 'lineAnchor selectedLine';
	}

	selectedLine = line;
}

(function() {
    'use strict';

    updateHighlightedLine();
    window.onhashchange = updateHighlightedLine;

    /* bail out if user is testing a version of this script via Greasemonkey or Tampermonkey */
    if (window.HAPI_ResponseHighlighter_userscript) {
        console.log("HAPI ResponseHighlighter: userscript detected - not executing embedded script");
        return;
    }

    /* adds hyperlinks and CSS styles to dates and UUIDs (e.g. to enable user-select: all) */
    const logicalReferenceRegex = /^[A-Z][A-Za-z]+\/[0-9]+$/;
    const dateTimeRegex = /^-?[0-9]{4}(-(0[1-9]|1[0-2])(-(0[0-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?(Z|(\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00)))?)?)?$/; // from the spec - https://www.hl7.org/fhir/datatypes.html#datetime
    const uuidRegex = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/;

    const allQuotes = document.querySelectorAll(".hlQuot");
    for (var i = 0; i < allQuotes.length; i++) {
        const quote = allQuotes[i];
        const text = quote.textContent.substr(1, quote.textContent.length - 2 /* remove quotes */);

        const absHyperlink = text.startsWith("http://") || text.startsWith("https://");
        const relHyperlink = text.match(logicalReferenceRegex);
        const uuid = text.match(uuidRegex);
        const dateTime = text.match(dateTimeRegex);

        if (absHyperlink || relHyperlink) {
            const link = document.createElement("a");
            const href = absHyperlink ? text : "FHIR_BASE/" + text;
            link.setAttribute("href", href);
            link.textContent = '"' + text + '"';
            quote.textContent = "";
            quote.appendChild(link);
        }

        if (uuid || dateTime) {
            const span = document.createElement("span");
            span.setAttribute("class", uuid ? "uuid" : "dateTime");
            span.textContent = text;
            quote.textContent = "";
            quote.appendChild(document.createTextNode('"'));
            quote.appendChild(span);
            quote.appendChild(document.createTextNode('"'));
        }
    }
})();<commit_msg>ResponseHighlighter - added performance logging to the browser console<commit_after>
var selectedLines = new Array();
function updateHighlightedLine() {
	updateHighlightedLineTo(window.location.hash);
}

function updateHighlightedLineTo(theNewHash) {

	for (var next in selectedLines) {
		document.getElementById('line' + selectedLines[next]).className = '';
		document.getElementById('anchor' + selectedLines[next]).className = 'lineAnchor';
	}
	selectedLines = new Array();

	var line = -1;
	if (theNewHash && theNewHash.match('L[0-9]+-L[0-9]+')) {
		var dashIndex = theNewHash.indexOf('-');
		var start = parseInt(theNewHash.substring(2, dashIndex));
		var end = parseInt(theNewHash.substring(dashIndex+2));
		for (var i = start; i <= end; i++) {
			selectedLines.push(i);
		}
	} else if (theNewHash && theNewHash.match('L[0-9]+')) {
		var line = parseInt(theNewHash.substring(2));
		selectedLines.push(line);
	}

	for (var next in selectedLines) {
		// Prevent us from scrolling to the selected line
		document.getElementById('L' + selectedLines[next]).name = '';
		// Select the line number column
		document.getElementById('line' + selectedLines[next]).className = 'selectedLine';
		// Select the response body column
		document.getElementById('anchor' + selectedLines[next]).className = 'lineAnchor selectedLine';
	}

	selectedLine = line;
}

function updateHyperlinksAndStyles() {
    /* adds hyperlinks and CSS styles to dates and UUIDs (e.g. to enable user-select: all) */
    const logicalReferenceRegex = /^[A-Z][A-Za-z]+\/[0-9]+$/;
    const dateTimeRegex = /^-?[0-9]{4}(-(0[1-9]|1[0-2])(-(0[0-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?(Z|(\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00)))?)?)?$/; // from the spec - https://www.hl7.org/fhir/datatypes.html#datetime
    const uuidRegex = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/;

    const allQuotes = document.querySelectorAll(".hlQuot");
    for (var i = 0; i < allQuotes.length; i++) {
        const quote = allQuotes[i];
        const text = quote.textContent.substr(1, quote.textContent.length - 2 /* remove quotes */);

        const absHyperlink = text.startsWith("http://") || text.startsWith("https://");
        const relHyperlink = text.match(logicalReferenceRegex);
        const uuid = text.match(uuidRegex);
        const dateTime = text.match(dateTimeRegex);

        if (absHyperlink || relHyperlink) {
            const link = document.createElement("a");
            const href = absHyperlink ? text : "FHIR_BASE/" + text;
            link.setAttribute("href", href);
            link.textContent = '"' + text + '"';
            quote.textContent = "";
            quote.appendChild(link);
        }

        if (uuid || dateTime) {
            const span = document.createElement("span");
            span.setAttribute("class", uuid ? "uuid" : "dateTime");
            span.textContent = text;
            quote.textContent = "";
            quote.appendChild(document.createTextNode('"'));
            quote.appendChild(span);
            quote.appendChild(document.createTextNode('"'));
        }
    }
}

(function() {
    'use strict';

    /* bail out if user is testing a version of this script via Greasemonkey or Tampermonkey */
    if (window.HAPI_ResponseHighlighter_userscript) {
        console.log("HAPI ResponseHighlighter: userscript detected - not executing embedded script");
        return;
    }

    console.time("updateHighlightedLine");
    updateHighlightedLine();
    console.timeEnd("updateHighlightedLine");
    window.onhashchange = updateHighlightedLine;

    console.time("updateHyperlinksAndStyles");
    updateHyperlinksAndStyles();
    console.timeEnd("updateHyperlinksAndStyles");

    window.addEventListener("load", function(event) {
      // https://developer.mozilla.org/en-US/docs/Web/API/Navigation_timing_API
      var now = new Date().getTime();
      var page_load_time = now - performance.timing.navigationStart;
      console.log("User-perceived page loading time: " + page_load_time + "ms");
    });

})();
<|endoftext|>


<commit_before>var contactArray = [];
// When a new Contact is created or imported from memory, it accepts an object literal
//  which can have a wide variety of properties. The only properties that are mandatory are
//  a first or last name.
function Contact(newInfo) {
  for (property in newInfo) {
    this[property] = newInfo[property];
  }
  // If either of these are missing, the construction cannot proceed.
  if (this.sortName == '') {
    console.error('Error creating Contact:');
    console.info(newInfo);
    return;
  }
  this.id = newInfo.id;

  if (!this.id) {
    this.id = getUniqueId();
    this.save();
  }
}

// Each object has the ability and responsibility to save itself to persistent storage.
Contact.prototype.save = function() {
  localStorage[this.id] = JSON.stringify(this);
};

// This method takes a number of days and pushes off the next scheduled contact
//  day until that many days after today.
Contact.prototype.postpone = function(days) {
  var newDate = new Date();
  newDate.setDate(newDate.getDate() + days);
  newDate.setHours(
    this.next.getHours(),
    this.next.getMinutes(),
    this.next.getSeconds(),
    this.next.getMilliseconds()
  );
  this.next = newDate;
};

// When the user hits "done" on a contact, it's basically the same as postponing
//  them for their assigned number of days. We might add more data her for metrics
//  such as number of times postponed vs reset, and we re-examine this method at
//  that time.
Contact.prototype.reset = function() {
  this.postpone(this.reachOut);
};

// A quick method of getting a contact's initials for the alt list icon.
Contact.prototype.initials = function() {
  return this.firstName[0] + this.lastName[0];
};

// Simply returns an objects lastname and firstname as a single string.
Contact.prototype.sortName = function() {
  return this.lastName + this.firstName;
};

// WHen an object first gets created, it needs a unique identifier.
function getUniqueId() {
  // Start searching at ID 0
  var newId = 0;

  // Process the existing IDs, looking for the existence of the current iteration of newId.
  //  This will exit with an unused ID number.
  while ( idSearch(contactArray,newId) ) {
    newId++;
  }

  // This is the function that searches the Contact array for the existence of a given ID number.
  //  It returns null when a number is not used.
  function idSearch(array,id) {
    return array.filter(function ( obj ) {
      return obj.id == id;
    })[0];
  }

  return newId;
}

// Each object has the ability to remove itself from storage and the memory array.
Contact.prototype.removeContact = function() {
  // For the contactArray we have to go through each one looking for the right id
  for (var i = 0; i < contactArray.length; i++) {
    if (contactArray[i].id == this.id) {
      contactArray.splice(i,1);
      // For localStorage we can just say which one to remove.
      delete localStorage[this.id];
      // Easy way to exit the for loop. Maybe also useful for error detection?
      return true;
    }
  }
  return false;
};

// When each page loads, it need the object data from persistent storage.
loadDataFromStorage();

function loadDataFromStorage() {
  for (object in localStorage) {
    var newContact = new Contact(JSON.parse(localStorage[object]));
    contactArray.push(newContact);
  }
}

// This is a function to make a new Contact object, but it might also be
//  adapted to update a current object already in the array and storage.
function addContact(submitObject) {
  // construct and push object to array
  var newContact = new Contact(submitObject);
  contactArray.push(newContact);
}

// If there's nothing in storage, this will generate demo contacts.
populateDemoContacts();

function populateDemoContacts() {
  if (localStorage.length == 0) {
    for (var i = 0; i < demoContacts.length; i++) {
      var newContact = new Contact(demoContacts[i]);
      contactArray.push(newContact);
    }
  }
}

// This should be an easy way to call a specific contact from the array by id
function contact(id) {
  for (var i = 0; i < contactArray.length; i++) {
    if (contactArray[i].id = id) {
      return contactArray[i];
    }
  }
  return false;
}
<commit_msg>typo fix<commit_after>var contactArray = [];
// When a new Contact is created or imported from memory, it accepts an object literal
//  which can have a wide variety of properties. The only properties that are mandatory are
//  a first or last name.
function Contact(newInfo) {
  for (property in newInfo) {
    this[property] = newInfo[property];
  }
  // If either of these are missing, the construction cannot proceed.
  if (this.sortName == '') {
    console.error('Error creating Contact:');
    console.info(newInfo);
    return;
  }
  this.id = newInfo.id;

  if (!this.id) {
    this.id = getUniqueId();
    this.save();
  }
}

// Each object has the ability and responsibility to save itself to persistent storage.
Contact.prototype.save = function() {
  localStorage[this.id] = JSON.stringify(this);
};

// This method takes a number of days and pushes off the next scheduled contact
//  day until that many days after today.
Contact.prototype.postpone = function(days) {
  var newDate = new Date();
  newDate.setDate(newDate.getDate() + days);
  newDate.setHours(
    this.next.getHours(),
    this.next.getMinutes(),
    this.next.getSeconds(),
    this.next.getMilliseconds()
  );
  this.next = newDate;
};

// When the user hits "done" on a contact, it's basically the same as postponing
//  them for their assigned number of days. We might add more data her for metrics
//  such as number of times postponed vs reset, and we re-examine this method at
//  that time.
Contact.prototype.reset = function() {
  this.postpone(this.reachOut);
};

// A quick method of getting a contact's initials for the alt list icon.
Contact.prototype.initials = function() {
  return this.firstName[0] + this.lastName[0];
};

// Simply returns an objects lastname and firstname as a single string.
Contact.prototype.sortName = function() {
  return this.lastName + this.firstName;
};

// WHen an object first gets created, it needs a unique identifier.
function getUniqueId() {
  // Start searching at ID 0
  var newId = 0;

  // Process the existing IDs, looking for the existence of the current iteration of newId.
  //  This will exit with an unused ID number.
  while ( idSearch(contactArray,newId) ) {
    newId++;
  }

  // This is the function that searches the Contact array for the existence of a given ID number.
  //  It returns null when a number is not used.
  function idSearch(array,id) {
    return array.filter(function ( obj ) {
      return obj.id == id;
    })[0];
  }

  return newId;
}

// Each object has the ability to remove itself from storage and the memory array.
Contact.prototype.removeContact = function() {
  // For the contactArray we have to go through each one looking for the right id
  for (var i = 0; i < contactArray.length; i++) {
    if (contactArray[i].id == this.id) {
      contactArray.splice(i,1);
      // For localStorage we can just say which one to remove.
      delete localStorage[this.id];
      // Easy way to exit the for loop. Maybe also useful for error detection?
      return true;
    }
  }
  return false;
};

// When each page loads, it need the object data from persistent storage.
loadDataFromStorage();

function loadDataFromStorage() {
  for (object in localStorage) {
    var newContact = new Contact(JSON.parse(localStorage[object]));
    contactArray.push(newContact);
  }
}

// This is a function to make a new Contact object, but it might also be
//  adapted to update a current object already in the array and storage.
function addContact(submitObject) {
  // construct and push object to array
  var newContact = new Contact(submitObject);
  contactArray.push(newContact);
}

// If there's nothing in storage, this will generate demo contacts.
populateDemoContacts();

function populateDemoContacts() {
  if (localStorage.length == 0) {
    for (var i = 0; i < demoContacts.length; i++) {
      var newContact = new Contact(demoContacts[i]);
      contactArray.push(newContact);
    }
  }
}

// This should be an easy way to call a specific contact from the array by id
function contact(id) {
  for (var i = 0; i < contactArray.length; i++) {
    if (contactArray[i].id == id) {
      return contactArray[i];
    }
  }
  return false;
}
<|endoftext|>