<commit_before>var app = angular.module("relationshipService", []);

/**
 * Relationship Service Provider
 */
app.factory('$relationshipService', function($http, config, $authenticationService) {

    // Query cache
    var cache = {
        full_count: 0,
        pagination: {
            skip: 0,
            limit: 50
        },
        filter: {
            search_text: ""
        }
    };

    return {
        init: function(relationship_label, relationship_type){
            var new_object;
            switch (relationship_label){
                case 'belongs_to': {
                    new_object = {
                        scenario_id: null
                    };
                    switch (relationship_type){
                        case 'location': {
                            new_object.location_id = null;
                            break;
                        }
                        case 'video': {
                            new_object.video_id = null;
                            break;
                        }
                        case 'overlay': {
                            new_object.overlay_id = null;
                            break;
                        }
                    }
                    break;
                }
                case 'connected_to': {
                    new_object = {
                        start_location_id: null,
                        end_location_id: null
                    };
                    break;
                }
                case 'embedded_in': {
                    new_object = {
                        overlay_id: null,
                        video_id: null,
                        w: 300,
                        h: 200,
                        d: 0,
                        x: 1,
                        y: 1,
                        z: 0,
                        rx: 0,
                        ry: 0,
                        rz: 0,
                        display: true
                    };
                    break;
                }
                case 'has_parent_location': {
                    new_object = {
                        start_location_id: null,
                        end_location_id: null
                    };
                    break;
                }
                case 'recorded_at': {
                    new_object = {
                        video_id: null,
                        location_id: null,
                        preferred: true
                    };
                    break;
                }
            }
            return new_object;
        },
        get_types: function() {
            return [
                {
                    name: "belongs_to"
                }, {
                    name: "connected_to"
                }, {
                    name: "embedded_in"
                }, {
                    name: "has_parent_location"
                }, {
                    name: "recorded_at"
                }
            ];
        },
        getCount: function(){
            return cache.full_count;
        },
        getFilter: function(){
            return cache.filter;
        },
        getPagination: function(){
            return cache.pagination;
        },
        setCount: function(data) {
            cache.full_count = data;
        },
        setFilter: function(data) {
            cache.filter = data;
        },
        setPagination: function(data) {
            cache.pagination = data;
        },
        list: function() {
            return $http.get(config.getApiEndpoint() + "/relationships");
        },
        list_by_type: function(relationship_label, relationship_type, pagination, filter) {
            // Initalize query
            var query = "?";

            // Add pagination to query
            if(pagination){
                if(pagination.skip && pagination.skip !== null){
                    query = query + "skip=" + pagination.skip + "&";
                }
                if(pagination.limit && pagination.limit !== null){
                    query = query + "limit=" + pagination.limit + "&";
                }
            }

            // Add filters to query
            if(filter){
                if(filter.filterName && filter.filterName !== null){
                    query = query + "filterName=" + filter.filterName + "&";
                }
            }

            // Finalize query
            query = query.slice(0, -1);

            // Apply relationship-label (& optional relationship-type)
            if(relationship_type){
                return $http.get(config.getApiEndpoint() + "/relationship/" + relationship_label + "/" + relationship_type + query);
            } else {
                return $http.get(config.getApiEndpoint() + "/relationship/" + relationship_label + query);
            }
        },
        search_by_type: function(relationship_label, relationship_type, pagination, filter) {
            // Initalize query
            var query = "?";

            // Add pagination to query
            if(pagination){
                if(pagination.skip && pagination.skip !== null){
                    query = query + "skip=" + pagination.skip + "&";
                }
                if(pagination.limit && pagination.limit !== null){
                    query = query + "limit=" + pagination.limit + "&";
                }
            }

            // Add filters to query
            if(filter){
                if(filter.filterName && filter.filterName !== null){
                    query = query + "filterName=" + filter.filterName + "&";
                }
            }

            // Finalize query
            query = query.slice(0, -1);

            // Apply relationship-label (& optional relationship-type)
            if(relationship_type){
                return $http.post(config.getApiEndpoint() + "/search/relationship/" + relationship_label + "/" + relationship_type + query, {
                    search_text: filter.search_text
                });
            } else {
                return $http.post(config.getApiEndpoint() + "/search/relationship/" + relationship_label + "/" + query, {
                    search_text: filter.search_text
                });
            }
        },
        retrieve_by_id: function(relationship_label, relationship_id, relationship_type) {
            if(label){
                return $http.get(config.getApiEndpoint() + "/relationship/" + relationship_label + "/" + relationship_id + "/" + relationship_type);
            } else {
                return $http.get(config.getApiEndpoint() + "/relationship/" + relationship_label + "/" + relationship_id);
            }
        },
        create: function(relationship_label, data, relationship_type) {
            if(relationship_type){
                return $http.post(config.getApiEndpoint() + "/relationship/" + relationship_label + "/" + relationship_type, data, {
                    headers: {
                        'Authorization': 'Bearer ' + $authenticationService.getToken(),
                        'Content-Type': 'application/json'
                    }
                });
            } else {
                return $http.post(config.getApiEndpoint() + "/relationship/" + relationship_label, data, {
                    headers: {
                        'Authorization': 'Bearer ' + $authenticationService.getToken(),
                        'Content-Type': 'application/json'
                    }
                });
            }
        },
        edit: function(relationship_label, relationship_id, relationship_type, data) {
            return $http.put(config.getApiEndpoint() + "/relationship/" + relationship_label + "/" + relationship_id + "/" + relationship_type, data, {
                headers: {
                    'Authorization': 'Bearer ' + $authenticationService.getToken(),
                    'Content-Type': 'application/json'
                }
            });
        },
        remove: function(relationship_id) {
            return $http.delete(config.getApiEndpoint() + "/relationships/" + relationship_id, {
                headers: {
                    'Authorization': 'Bearer ' + $authenticationService.getToken()
                }
            });
        }
    };

});
<commit_msg>Typo-fix<commit_after>var app = angular.module("relationshipService", []);

/**
 * Relationship Service Provider
 */
app.factory('$relationshipService', function($http, config, $authenticationService) {

    // Query cache
    var cache = {
        full_count: 0,
        pagination: {
            skip: 0,
            limit: 50
        },
        filter: {
            search_text: ""
        }
    };

    return {
        init: function(relationship_label, relationship_type){
            var new_object;
            switch (relationship_label){
                case 'belongs_to': {
                    new_object = {
                        scenario_id: null
                    };
                    switch (relationship_type){
                        case 'location': {
                            new_object.location_id = null;
                            break;
                        }
                        case 'video': {
                            new_object.video_id = null;
                            break;
                        }
                        case 'overlay': {
                            new_object.overlay_id = null;
                            break;
                        }
                    }
                    break;
                }
                case 'connected_to': {
                    new_object = {
                        start_location_id: null,
                        end_location_id: null
                    };
                    break;
                }
                case 'embedded_in': {
                    new_object = {
                        overlay_id: null,
                        video_id: null,
                        w: 300,
                        h: 200,
                        d: 0,
                        x: 1,
                        y: 1,
                        z: 0,
                        rx: 0,
                        ry: 0,
                        rz: 0,
                        display: true
                    };
                    break;
                }
                case 'has_parent_location': {
                    new_object = {
                        start_location_id: null,
                        end_location_id: null
                    };
                    break;
                }
                case 'recorded_at': {
                    new_object = {
                        video_id: null,
                        location_id: null,
                        preferred: true
                    };
                    break;
                }
            }
            return new_object;
        },
        get_types: function() {
            return [
                {
                    name: "belongs_to"
                }, {
                    name: "connected_to"
                }, {
                    name: "embedded_in"
                }, {
                    name: "has_parent_location"
                }, {
                    name: "recorded_at"
                }
            ];
        },
        getCount: function(){
            return cache.full_count;
        },
        getFilter: function(){
            return cache.filter;
        },
        getPagination: function(){
            return cache.pagination;
        },
        setCount: function(data) {
            cache.full_count = data;
        },
        setFilter: function(data) {
            cache.filter = data;
        },
        setPagination: function(data) {
            cache.pagination = data;
        },
        list: function() {
            return $http.get(config.getApiEndpoint() + "/relationships");
        },
        list_by_type: function(relationship_label, relationship_type, pagination, filter) {
            // Initalize query
            var query = "?";

            // Add pagination to query
            if(pagination){
                if(pagination.skip && pagination.skip !== null){
                    query = query + "skip=" + pagination.skip + "&";
                }
                if(pagination.limit && pagination.limit !== null){
                    query = query + "limit=" + pagination.limit + "&";
                }
            }

            // Add filters to query
            if(filter){
                if(filter.filterName && filter.filterName !== null){
                    query = query + "filterName=" + filter.filterName + "&";
                }
            }

            // Finalize query
            query = query.slice(0, -1);

            // Apply relationship-label (& optional relationship-type)
            if(relationship_type){
                return $http.get(config.getApiEndpoint() + "/relationship/" + relationship_label + "/" + relationship_type + query);
            } else {
                return $http.get(config.getApiEndpoint() + "/relationship/" + relationship_label + query);
            }
        },
        search_by_type: function(relationship_label, relationship_type, pagination, filter) {
            // Initalize query
            var query = "?";

            // Add pagination to query
            if(pagination){
                if(pagination.skip && pagination.skip !== null){
                    query = query + "skip=" + pagination.skip + "&";
                }
                if(pagination.limit && pagination.limit !== null){
                    query = query + "limit=" + pagination.limit + "&";
                }
            }

            // Add filters to query
            if(filter){
                if(filter.filterName && filter.filterName !== null){
                    query = query + "filterName=" + filter.filterName + "&";
                }
            }

            // Finalize query
            query = query.slice(0, -1);

            // Apply relationship-label (& optional relationship-type)
            if(relationship_type){
                return $http.post(config.getApiEndpoint() + "/search/relationship/" + relationship_label + "/" + relationship_type + query, {
                    search_text: filter.search_text
                });
            } else {
                return $http.post(config.getApiEndpoint() + "/search/relationship/" + relationship_label + "/" + query, {
                    search_text: filter.search_text
                });
            }
        },
        retrieve_by_id: function(relationship_label, relationship_id, relationship_type) {
            if(relationship_type){
                return $http.get(config.getApiEndpoint() + "/relationship/" + relationship_label + "/" + relationship_id + "/" + relationship_type);
            } else {
                return $http.get(config.getApiEndpoint() + "/relationship/" + relationship_label + "/" + relationship_id);
            }
        },
        create: function(relationship_label, data, relationship_type) {
            if(relationship_type){
                return $http.post(config.getApiEndpoint() + "/relationship/" + relationship_label + "/" + relationship_type, data, {
                    headers: {
                        'Authorization': 'Bearer ' + $authenticationService.getToken(),
                        'Content-Type': 'application/json'
                    }
                });
            } else {
                return $http.post(config.getApiEndpoint() + "/relationship/" + relationship_label, data, {
                    headers: {
                        'Authorization': 'Bearer ' + $authenticationService.getToken(),
                        'Content-Type': 'application/json'
                    }
                });
            }
        },
        edit: function(relationship_label, relationship_id, relationship_type, data) {
            return $http.put(config.getApiEndpoint() + "/relationship/" + relationship_label + "/" + relationship_id + "/" + relationship_type, data, {
                headers: {
                    'Authorization': 'Bearer ' + $authenticationService.getToken(),
                    'Content-Type': 'application/json'
                }
            });
        },
        remove: function(relationship_id) {
            return $http.delete(config.getApiEndpoint() + "/relationships/" + relationship_id, {
                headers: {
                    'Authorization': 'Bearer ' + $authenticationService.getToken()
                }
            });
        }
    };

});
<|endoftext|>


<commit_before>/*global describe, before, after, it */

// built-ins
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;

// devDependencies
var glob = require('glob');
var mkdirp = require('mkdirp');
var rimraf = require('rimraf');

// file to test
var template = require('../template');

// shared variables
var OUTPUT_DIR = __dirname + '/template-output';
var TEMPLATE   = path.resolve(__dirname, '..'); // grunt-init <path/to/template>
var GRUNT_INIT = path.join(TEMPLATE, 'node_modules/grunt-init/bin/grunt-init');

var EXPECTED_FILES = [
    "BUILD.md",
    "Gruntfile.js",
    "LICENSE-MIT",
    "README.md",
    "package.json",
    "src",
    "src/template-output",
    "src/template-output/HISTORY.md",
    "src/template-output/README.md",
    "src/template-output/build.json",
    "src/template-output/docs",
    "src/template-output/docs/component.json",
    "src/template-output/docs/index.mustache",
    "src/template-output/js",
    "src/template-output/js/template-output.js",
    "src/template-output/meta",
    "src/template-output/meta/template-output.json",
    "src/template-output/tests",
    "src/template-output/tests/unit",
    "src/template-output/tests/unit/assets",
    "src/template-output/tests/unit/assets/template-output-test.js",
    "src/template-output/tests/unit/index.html"
];

describe("templating", function () {
    this.slow('5s');

    before(function (done) {
        mkdirp(OUTPUT_DIR, done);
    });

    after(function (done) {
        rimraf(OUTPUT_DIR, done);
    });

    describe("module", function () {
        it("should create project if missing", function (done) {
            var child = spawn(GRUNT_INIT, [TEMPLATE], {
                cwd: OUTPUT_DIR,
                stdio: 'pipe'
            });

            child.stdout.setEncoding('utf8');
            child.stdout.on('data', answerPrompts(child));

            child.on('close', function (code) {
                filesCreated().should.eql(EXPECTED_FILES);
                done();
            });
        });

        it("should not overwrite existing project");

        it("should not overwrite existing module");
    });

    describe("project only", function () {
        before(function (done) {
            rimraf(OUTPUT_DIR, function () {
                mkdirp(OUTPUT_DIR, done);
            });
        });

        it("should not output module content", function (done) {
            var child = spawn(GRUNT_INIT, [TEMPLATE, '--project'], {
                cwd: OUTPUT_DIR,
                stdio: 'pipe'
            });

            child.stdout.setEncoding('utf8');
            child.stdout.on('data', answerPrompts(child));

            child.on('close', function (code) {
                filesCreated().should.eql(EXPECTED_FILES.slice(0, 5));
                done();
            });
        });
    });
});

function filesCreated(pattern) {
    return glob.sync(pattern || '**/*', { cwd: OUTPUT_DIR });
}

/**
Returns the "Project name" part of a "[?] Project name (foo)"
shell-escaped color prompt string.

@method getMessage
@param {String} prompt
@return {String}
**/
function getMessage(prompt) {
    var mStarts = prompt.indexOf('\u001b[90m') + 5;
    var mFinish = prompt.indexOf('\u001b[39m', mStarts);
    return prompt.substring(mStarts, mFinish);
}

/**
Answer the prompts via writing to a child's stdin.

If an answers object is provided, keys matching the
prompt's "message" property will answer thusly.

When no answer matches (or none is provided), the
default is accepted.

@method answerPrompts
@param {ChildProcess} child
@param {Object} [answers]
**/
function answerPrompts(child, answers) {
    answers = answers || {};
    return function (data) {
        var message,
            prompt = data.indexOf('[\u001b[32m?') === 0;
        // start of a prompt "[?]"
        if (prompt) {
            // send matching answer, or newline to accept default
            process.nextTick(function () {
                child.stdin.write((answers[getMessage(data)] || '') + '\n');
            });
        }
    };
}
<commit_msg>Add test case for unversioned build directory output.<commit_after>/*global describe, before, after, it */

// built-ins
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;

// devDependencies
var glob = require('glob');
var mkdirp = require('mkdirp');
var rimraf = require('rimraf');

// file to test
var template = require('../template');

// shared variables
var OUTPUT_DIR = __dirname + '/template-output';
var TEMPLATE   = path.resolve(__dirname, '..'); // grunt-init <path/to/template>
var GRUNT_INIT = path.join(TEMPLATE, 'node_modules/grunt-init/bin/grunt-init');

var EXPECTED_FILES = [
    "BUILD.md",
    "Gruntfile.js",
    "LICENSE-MIT",
    "README.md",
    "package.json",
    "src",
    "src/template-output",
    "src/template-output/HISTORY.md",
    "src/template-output/README.md",
    "src/template-output/build.json",
    "src/template-output/docs",
    "src/template-output/docs/component.json",
    "src/template-output/docs/index.mustache",
    "src/template-output/js",
    "src/template-output/js/template-output.js",
    "src/template-output/meta",
    "src/template-output/meta/template-output.json",
    "src/template-output/tests",
    "src/template-output/tests/unit",
    "src/template-output/tests/unit/assets",
    "src/template-output/tests/unit/assets/template-output-test.js",
    "src/template-output/tests/unit/index.html"
];

describe("templating", function () {
    this.slow('5s');

    before(function (done) {
        mkdirp(OUTPUT_DIR, done);
    });

    after(function (done) {
        rimraf(OUTPUT_DIR, done);
    });

    describe("module", function () {
        it("should create project if missing", function (done) {
            var child = spawn(GRUNT_INIT, [TEMPLATE], {
                cwd: OUTPUT_DIR,
                stdio: 'pipe'
            });

            child.stdout.setEncoding('utf8');
            child.stdout.on('data', answerPrompts(child));

            child.on('close', function (code) {
                filesCreated().should.eql(EXPECTED_FILES);
                done();
            });
        });

        it("should not overwrite existing project");

        it("should not overwrite existing module");
    });

    describe("project only", function () {
        before(function (done) {
            rimraf(OUTPUT_DIR, function () {
                mkdirp(OUTPUT_DIR, done);
            });
        });

        it("should not output module content", function (done) {
            var child = spawn(GRUNT_INIT, [TEMPLATE, '--project'], {
                cwd: OUTPUT_DIR,
                stdio: 'pipe'
            });

            child.stdout.setEncoding('utf8');
            child.stdout.on('data', answerPrompts(child));

            child.on('close', function (code) {
                filesCreated().should.eql(EXPECTED_FILES.slice(0, 5));
                done();
            });
        });

        describe("with unversioned build directory", function () {
            before(function (done) {
                var child = spawn(GRUNT_INIT, [TEMPLATE, '--project', '--force'], {
                    cwd: OUTPUT_DIR,
                    stdio: 'pipe'
                });

                child.stdout.setEncoding('utf8');
                child.stdout.on('data', answerPrompts(child, {
                    'Version build directory?': 'n'
                }));

                child.on('close', function (code) {
                    done();
                });
            });

            it("should include build/ in .gitignore", function () {
                var ignores = fs.readFileSync(path.join(OUTPUT_DIR, '.gitignore'), 'utf8');

                ignores.should.equal([
                    'node_modules/',
                    'build/',
                    'coverage/',
                    'release/',
                    ''
                ].join('\n'));
            });
        });
    });
});

function filesCreated(pattern) {
    return glob.sync(pattern || '**/*', { cwd: OUTPUT_DIR });
}

/**
Returns the "Project name" part of a "[?] Project name (foo)"
shell-escaped color prompt string.

@method getMessage
@param {String} prompt
@return {String}
**/
function getMessage(prompt) {
    var mStarts = prompt.indexOf('\u001b[90m') + 5;
    var mFinish = prompt.indexOf('\u001b[39m', mStarts);
    return prompt.substring(mStarts, mFinish);
}

/**
Answer the prompts via writing to a child's stdin.

If an answers object is provided, keys matching the
prompt's "message" property will answer thusly.

When no answer matches (or none is provided), the
default is accepted.

@method answerPrompts
@param {ChildProcess} child
@param {Object} [answers]
**/
function answerPrompts(child, answers) {
    answers = answers || {};
    return function (data) {
        var message,
            prompt = data.indexOf('[\u001b[32m?') === 0;
        // start of a prompt "[?]"
        if (prompt) {
            // send matching answer, or newline to accept default
            process.nextTick(function () {
                child.stdin.write((answers[getMessage(data)] || '') + '\n');
            });
        }
    };
}
<|endoftext|>


<commit_before>new Test.Unit.Runner({
  setup: function() {
    $('content').update(
      "Lorem ipsum dolor sit amet, " +
      "consectetuer adipiscing elit."
    );

    this.range = document.createRange();
    this.range.selectNode($('content'));
  },

  testSetStart: function() {
    var runner = this;

    this.range.setStart($('content'), 2);
    this.range.setEnd($('content'), 2);

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(2, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "endContainer");
    runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(true, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStart($('lorem'), 0);
    this.range.setEnd($('lorem'), 1);

    runner.assertEqual($('lorem'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('lorem'), this.range.endContainer, "endContainer");
    runner.assertEqual(1, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSetEnd: function() {
    var runner = this;

    this.range.setStart($('content'), 1);
    this.range.setEnd($('content'), 2);

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(1, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStart($('consectetuer'), 0);
    this.range.setEnd($('consectetuer'), 1);

    runner.assertEqual($('consectetuer'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('consectetuer'), this.range.endContainer, "startContainer");
    runner.assertEqual(1, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('consectetuer'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSetStartBefore: function() {
    var runner = this;

    this.range.setStartBefore($('lorem'));
    this.range.setEnd($('content'), 2);

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "endContainer");
    runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStartBefore($('content'));
    this.range.setEnd($('content'), 2);

    runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer");
    // runner.assertEqual(1, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "endContainer");
    runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSetStartAfter: function() {
    var runner = this;

    this.range.setStartAfter($('lorem'));
    this.range.setEnd($('content'), 2);

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(1, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStartAfter($('content'));
    this.range.setEnd($('content'), 2);

    // runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    // runner.assertEqual(2, this.range.startOffset, "startOffset");
    // runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    // runner.assertEqual(2, this.range.endOffset, "endOffset");
    // runner.assertEqual(true, this.range.collapsed, "collapsed");
    // runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSetEndBefore: function() {
    var runner = this;

    this.range.setStart($('content'), 0);
    this.range.setEndBefore($('lorem'));

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(0, this.range.endOffset, "endOffset");
    runner.assertEqual(true, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStart($('content'), 0);
    this.range.setEndBefore($('content'));

    // runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer");
    // runner.assertEqual(1, this.range.startOffset, "startOffset");
    // runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer");
    // runner.assertEqual(1, this.range.endOffset, "endOffset");
    // runner.assertEqual(true, this.range.collapsed, "collapsed");
    // runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSetEndAfter: function() {
    var runner = this;

    this.range.setStart($('content'), 0);
    this.range.setEndAfter($('lorem'));

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(1, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStart($('content'), 0);
    this.range.setEndAfter($('content'));

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer");
    // runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testCollapse: function() {
    var runner = this;

    this.range.setStart($('content'), 1);
    this.range.setEnd($('content'), 2);
    this.range.collapse(true);

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(1, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(1, this.range.endOffset, "endOffset");
    runner.assertEqual(true, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStart($('content'), 1);
    this.range.setEnd($('content'), 2);
    this.range.collapse(false);

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(2, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(true, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSelectNode: function() {
    var runner = this;

    this.range.selectNode($('content'));

    runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer");
    // runner.assertEqual(1, this.range.startOffset, "startOffset");
    runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer");
    // runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.selectNode($('lorem'));

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(1, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSelectNodeContents: function() {
    var runner = this;

    this.range.selectNodeContents($('content'));

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(4, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.selectNodeContents($('lorem'));

    runner.assertEqual($('lorem'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('lorem'), this.range.endContainer, "startContainer");
    runner.assertEqual(1, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testDeleteContents: function() {
    var runner = this;

    this.range.selectNodeContents($('lorem'));
    this.range.deleteContents();

    runner.assertEqual("", $('lorem').innerHTML, "innerHTML");
  },

  testExtractContents: function() {
    var runner = this;

    this.range.selectNodeContents($('lorem'));
    var contents = this.range.extractContents();

    runner.assertEqual("", $('lorem').innerHTML, "innerHTML");

    // IE document does not have any useful methods. Everyone else can just
    // read textContent, IE needs to append the fragment to another element
    // and read its innerHTML
    if (contents.textContent) {
      runner.assertEqual("Lorem ipsum", contents.textContent, "textContent");
    } else {
      var e = new Element('div');
      e.appendChild(contents);
      runner.assertEqual("Lorem ipsum", e.innerHTML, "textContent");
    }
  },

  testCloneContents: function() {
    var runner = this;

    this.range.selectNodeContents($('lorem'));
    var contents = this.range.cloneContents();

    runner.assertEqual("Lorem ipsum", $('lorem').innerHTML, "innerHTML");

    // IE document does not have any useful methods. Everyone else can just
    // read textContent, IE needs to append the fragment to another element
    // and read its innerHTML
    if (contents.textContent) {
      runner.assertEqual("Lorem ipsum", contents.textContent, "textContent");
    } else {
      var e = new Element('div');
      e.appendChild(contents);
      runner.assertEqual("Lorem ipsum", e.innerHTML, "textContent");
    }
  },

  testInsertNode: function() {
    var runner = this;

    var node = new Element('span', {id: 'inserted'}).update("inserted!");

    this.range.selectNode($('lorem'));
    this.range.insertNode(node);

    runner.assertEqual("inserted!", $('inserted').innerHTML, "innerHTML");

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    // runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSurrondContents: function() {
    var runner = this;

    var node;

    node = new Element('span', {id: 'wrapper'});

    this.range.selectNodeContents($('lorem'));
    this.range.surroundContents(node);

    expected = new Element('div');
    expected.appendChild(new Element('span', {id: 'wrapper'}).update("Lorem ipsum"));

    runner.assertEqual(expected.innerHTML, $('lorem').innerHTML, "innerHTML");
  },

  testEqualRange: function() {
    var runner = this;

    if (!this.range.equalRange) {
      runner.flunk("equalRange is not implemented");
      return false;
    }

    var r1 = document.createRange();
    r1.selectNodeContents($('lorem'));

    var r2 = document.createRange();
    r2.selectNodeContents($('lorem'));

    var r3 = document.createRange();
    r3.selectNodeContents($('consectetuer'));

    runner.assert(r1.equalRange(r1), "r1.equalRange(r1)");
    runner.assert(r2.equalRange(r2), "r2.equalRange(r2)");
    runner.assert(r3.equalRange(r3), "r3.equalRange(r3)");

    runner.assert(r1.equalRange(r2), "r1.equalRange(r2)");
    runner.assert(r2.equalRange(r1), "r2.equalRange(r1)");
    runner.assert(!r1.equalRange(r3), "r1.equalRange(r3)");
    runner.assert(!r3.equalRange(r1), "r3.equalRange(r1)");

    runner.assert(!r1.equalRange(null), "r1.equalRange(null)");
    runner.assert(!r2.equalRange(null), "r2.equalRange(null)");
    runner.assert(!r3.equalRange(null), "r3.equalRange(null)");
  },

  testGetNode: function() {
    var runner = this;

    if (!this.range.getNode) {
      runner.flunk("getNode is not implemented");
      return false;
    }

    this.range.selectNodeContents($('lorem'));
    runner.assertEqual($('lorem'), this.range.getNode(), "getNode");

    this.range.selectNode($('lorem'));
    runner.assertEqual($('lorem'), this.range.getNode(), "getNode");

    this.range.setStart($('lorem'), 0);
    this.range.setEnd($('lorem'), 1);
    runner.assertEqual($('lorem'), this.range.getNode(), "getNode");
  }
});
<commit_msg>clean test editor whitespace<commit_after>new Test.Unit.Runner({
  setup: function() {
    $('content').update(
      "Lorem ipsum dolor sit amet, " +
      "consectetuer adipiscing elit."
    );

    $('wrapper').cleanWhitespace();

    this.range = document.createRange();
    this.range.selectNode($('content'));
  },

  testSetStart: function() {
    var runner = this;

    this.range.setStart($('content'), 2);
    this.range.setEnd($('content'), 2);

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(2, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "endContainer");
    runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(true, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStart($('lorem'), 0);
    this.range.setEnd($('lorem'), 1);

    runner.assertEqual($('lorem'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('lorem'), this.range.endContainer, "endContainer");
    runner.assertEqual(1, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSetEnd: function() {
    var runner = this;

    this.range.setStart($('content'), 1);
    this.range.setEnd($('content'), 2);

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(1, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStart($('consectetuer'), 0);
    this.range.setEnd($('consectetuer'), 1);

    runner.assertEqual($('consectetuer'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('consectetuer'), this.range.endContainer, "startContainer");
    runner.assertEqual(1, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('consectetuer'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSetStartBefore: function() {
    var runner = this;

    this.range.setStartBefore($('lorem'));
    this.range.setEnd($('content'), 2);

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "endContainer");
    runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStartBefore($('content'));
    this.range.setEnd($('content'), 2);

    runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer");
    // runner.assertEqual(1, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "endContainer");
    runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSetStartAfter: function() {
    var runner = this;

    this.range.setStartAfter($('lorem'));
    this.range.setEnd($('content'), 2);

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(1, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStartAfter($('content'));
    this.range.setEnd($('content'), 2);

    // runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    // runner.assertEqual(2, this.range.startOffset, "startOffset");
    // runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    // runner.assertEqual(2, this.range.endOffset, "endOffset");
    // runner.assertEqual(true, this.range.collapsed, "collapsed");
    // runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSetEndBefore: function() {
    var runner = this;

    this.range.setStart($('content'), 0);
    this.range.setEndBefore($('lorem'));

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(0, this.range.endOffset, "endOffset");
    runner.assertEqual(true, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStart($('content'), 0);
    this.range.setEndBefore($('content'));

    // runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer");
    // runner.assertEqual(1, this.range.startOffset, "startOffset");
    // runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer");
    // runner.assertEqual(1, this.range.endOffset, "endOffset");
    // runner.assertEqual(true, this.range.collapsed, "collapsed");
    // runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSetEndAfter: function() {
    var runner = this;

    this.range.setStart($('content'), 0);
    this.range.setEndAfter($('lorem'));

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(1, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStart($('content'), 0);
    this.range.setEndAfter($('content'));

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer");
    // runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testCollapse: function() {
    var runner = this;

    this.range.setStart($('content'), 1);
    this.range.setEnd($('content'), 2);
    this.range.collapse(true);

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(1, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(1, this.range.endOffset, "endOffset");
    runner.assertEqual(true, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.setStart($('content'), 1);
    this.range.setEnd($('content'), 2);
    this.range.collapse(false);

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(2, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(true, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSelectNode: function() {
    var runner = this;

    this.range.selectNode($('content'));

    runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer");
    // runner.assertEqual(1, this.range.startOffset, "startOffset");
    runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer");
    // runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.selectNode($('lorem'));

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(1, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSelectNodeContents: function() {
    var runner = this;

    this.range.selectNodeContents($('content'));

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    runner.assertEqual(4, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");

    this.range.selectNodeContents($('lorem'));

    runner.assertEqual($('lorem'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('lorem'), this.range.endContainer, "startContainer");
    runner.assertEqual(1, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testDeleteContents: function() {
    var runner = this;

    this.range.selectNodeContents($('lorem'));
    this.range.deleteContents();

    runner.assertEqual("", $('lorem').innerHTML, "innerHTML");
  },

  testExtractContents: function() {
    var runner = this;

    this.range.selectNodeContents($('lorem'));
    var contents = this.range.extractContents();

    runner.assertEqual("", $('lorem').innerHTML, "innerHTML");

    // IE document does not have any useful methods. Everyone else can just
    // read textContent, IE needs to append the fragment to another element
    // and read its innerHTML
    if (contents.textContent) {
      runner.assertEqual("Lorem ipsum", contents.textContent, "textContent");
    } else {
      var e = new Element('div');
      e.appendChild(contents);
      runner.assertEqual("Lorem ipsum", e.innerHTML, "textContent");
    }
  },

  testCloneContents: function() {
    var runner = this;

    this.range.selectNodeContents($('lorem'));
    var contents = this.range.cloneContents();

    runner.assertEqual("Lorem ipsum", $('lorem').innerHTML, "innerHTML");

    // IE document does not have any useful methods. Everyone else can just
    // read textContent, IE needs to append the fragment to another element
    // and read its innerHTML
    if (contents.textContent) {
      runner.assertEqual("Lorem ipsum", contents.textContent, "textContent");
    } else {
      var e = new Element('div');
      e.appendChild(contents);
      runner.assertEqual("Lorem ipsum", e.innerHTML, "textContent");
    }
  },

  testInsertNode: function() {
    var runner = this;

    var node = new Element('span', {id: 'inserted'}).update("inserted!");

    this.range.selectNode($('lorem'));
    this.range.insertNode(node);

    runner.assertEqual("inserted!", $('inserted').innerHTML, "innerHTML");

    runner.assertEqual($('content'), this.range.startContainer, "startContainer");
    runner.assertEqual(0, this.range.startOffset, "startOffset");
    runner.assertEqual($('content'), this.range.endContainer, "startContainer");
    // runner.assertEqual(2, this.range.endOffset, "endOffset");
    runner.assertEqual(false, this.range.collapsed, "collapsed");
    runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer");
  },

  testSurrondContents: function() {
    var runner = this;

    var node;

    node = new Element('span', {id: 'wrapper'});

    this.range.selectNodeContents($('lorem'));
    this.range.surroundContents(node);

    expected = new Element('div');
    expected.appendChild(new Element('span', {id: 'wrapper'}).update("Lorem ipsum"));

    runner.assertEqual(expected.innerHTML, $('lorem').innerHTML, "innerHTML");
  },

  testEqualRange: function() {
    var runner = this;

    if (!this.range.equalRange) {
      runner.flunk("equalRange is not implemented");
      return false;
    }

    var r1 = document.createRange();
    r1.selectNodeContents($('lorem'));

    var r2 = document.createRange();
    r2.selectNodeContents($('lorem'));

    var r3 = document.createRange();
    r3.selectNodeContents($('consectetuer'));

    runner.assert(r1.equalRange(r1), "r1.equalRange(r1)");
    runner.assert(r2.equalRange(r2), "r2.equalRange(r2)");
    runner.assert(r3.equalRange(r3), "r3.equalRange(r3)");

    runner.assert(r1.equalRange(r2), "r1.equalRange(r2)");
    runner.assert(r2.equalRange(r1), "r2.equalRange(r1)");
    runner.assert(!r1.equalRange(r3), "r1.equalRange(r3)");
    runner.assert(!r3.equalRange(r1), "r3.equalRange(r1)");

    runner.assert(!r1.equalRange(null), "r1.equalRange(null)");
    runner.assert(!r2.equalRange(null), "r2.equalRange(null)");
    runner.assert(!r3.equalRange(null), "r3.equalRange(null)");
  },

  testGetNode: function() {
    var runner = this;

    if (!this.range.getNode) {
      runner.flunk("getNode is not implemented");
      return false;
    }

    this.range.selectNodeContents($('lorem'));
    runner.assertEqual($('lorem'), this.range.getNode(), "getNode");

    this.range.selectNode($('lorem'));
    runner.assertEqual($('lorem'), this.range.getNode(), "getNode");

    this.range.setStart($('lorem'), 0);
    this.range.setEnd($('lorem'), 1);
    runner.assertEqual($('lorem'), this.range.getNode(), "getNode");
  }
});
<|endoftext|>


<commit_before>

/* A calculator object for the Float texture based GEMM

	gl - WebGL object

	* uploads and downloads data
	* executes calculation
 */
function GEMMFloatCalculator(webgl){
	this.webgl = webgl;

	// create the webgl shader program for this calculation
	// based on the specific fragment shader for this calculation
	// and the generic pass through shader
	this.program = this.webgl.createProgram(GEMMFloatCalculator.FRAGMENT_SHADER);

}

module.exports = GEMMFloatCalculator;

GEMMFloatCalculator.TEXTURE_UNIFORM_NAME_1 = "A";
GEMMFloatCalculator.TEXTURE_UNIFORM_NAME_2 = "B_t";

GEMMFloatCalculator.FRAGMENT_SHADER = "                                  \n\
// fragment shader that calculates the sum of the passed row and         \n\
// column (texture coord).                                               \n\
// we loop over the row and column and sum the product.                  \n\
// product is then rendered to 32-bit IEEE754 floating point in the      \n\
// output RGBA canvas.                                                   \n\
// readPixel is used to read the bytes.                                  \n\
																		 \n\
precision highp float;                                                   \n\
																		 \n\
varying vec2      outTex;	// row, column to calculate                  \n\
uniform sampler2D A;		// padded A                                  \n\
uniform sampler2D B_t;		// padded B transpose                        \n\
uniform int       K;		// interior (shared) dimension               \n\
float delta = 1./float(K);	// space between items in shared dimension   \n\
																		 \n\
// sum row i x col j                                                     \n\
float sumrowcol(float i, float j) {                                      \n\
	float sum = 0.;			// sum for this row/column pair              \n\
	float k = 0.5 * delta;	// position on source texture                \n\
	for (int l=0 ; l<2048 ; ++l) {                                       \n\
		if(l >= K / 4) break;    // stop when we finish the row/column   \n\
																		 \n\
		// retrieve next four elements from each texture                 \n\
		vec4 a_ik = texture2D(  A, vec2(k, i));                          \n\
		vec4 b_kj = texture2D(B_t, vec2(k, j));                          \n\
																		 \n\
		// use `dot` to process four elements at a time                  \n\
		sum += dot(a_ik, b_kj);                                          \n\
		k += (delta * 4.0);      // (k * 4 + 0.5)*delta                  \n\
	}                                                                    \n\
	return sum;                                                          \n\
}                                                                        \n\
																		 \n\
// Render float to bytes according to IEEE 754 Floating Point            \n\
vec4 encodeFloat(float val) {                                            \n\
																		 \n\
	// TODO: correctly handle denormal numbers                           \n\
	// http://www.2ality.com/2012/04/number-encoding.html                \n\
	float a = abs(val);                           // encode absolute value + sign \n\
	float exp = floor(log2(a));                 // number of powers of 2 \n\
	float mant = pow(2.,log2(a)-exp) * pow(2.,23.);  // multiply to fill 24 bits (implied leading 1) \n\
	float mant1 = floor(mant / 256. / 256.);    // first 8 bits of mantissa \n\
	float mant2 = mod(floor(mant / 256.),256.); // second 8 bits         \n\
	float mant3 = mod(mant,256.);               // third 8 bits          \n\
																		 \n\
	highp float sign = 128.-128.*(a/val);			// sign bit is 256 or 0  \n\
	highp float e = (sign+exp+127.)/510.;		// exponent and sign     \n\
	highp float m1 = (mant1-(128.*(1.-mod(exp+127.,2.))))/255.; // handle leading bit \n\
	highp float m2 = (mant2)/255.;				// middle part           \n\
	highp float m3 = (mant3+.5)/255.;			// scale to 0 - 255      \n\
																		 \n\
	return vec4(m3,m2,m1,e);                                             \n\
}                                                                        \n\
																		 \n\
void main(void) {                                                        \n\
																		 \n\
	 // get the implied row and column from .y and .x                    \n\
	 // of passed (output) texel                                         \n\
 	float row = outTex.y;                                                \n\
	float col = outTex.x;                                                \n\
																		 \n\
	// sum row x col for the passed pixel                                \n\
	float sum = sumrowcol(row,col);                                      \n\
																		 \n\
	if (sum == 0.) {                                                     \n\
		gl_FragColor = vec4(0.,0.,0.,0.);                                \n\
		return;                                                          \n\
	}                                                                    \n\
																		 \n\
 	// output an IEEE754 32-bit floating point number 				  	 \n\
	gl_FragColor = encodeFloat(sum);									 \n\
}                                                                        \n\
";

/* Pack the given matrix data into texel layout for use by texture shader.
   This layout places each matrix in a different plane of the texture,
   one in 'r' the second in 'g'
 */
GEMMFloatCalculator.packData = function(data, r, c, transpose) {

	var mod = (c % 4),
		pad = 4 - mod;

	if (mod === 0) {
		// special case if column count is a multiple of four
		//console.log("mod == 0, returning original data");
		return data;
	}

	// dimensions
	var BYTES_PER_TEXEL = 4; // RGBA: one byte per channel, four bytes per texel
	var dst = 0,
		src = 0,
		p = 0;

	var texelcount = r*(c + pad);
	// get texel data (rgb) as a Float32Array
	var texels = new Float32Array(texelcount);

	// pad each row to multiple of four, with zeros
	for(dst = 0, src = 0; dst < texelcount; dst++, src++){

		if(src != 0 && src % c == 0){
			// reached the end of a source column, pad with zeros
			for(p = 0; p < pad; p++){
				texels[dst + p] = 0.0;
			}
			dst += pad;
		}

		// copy the real data
		texels[dst] = data[src];

	}

	return texels;
};

/* Create a texture from the given texel data and bind it to our shader program.
  must set up shader first
*/
GEMMFloatCalculator.prototype.bindInputTexture = function(h, w, texels, textureUnit, name){
	var gl = this.webgl.context,
		program = this.program;

	// create the texture from our floats
	var texture = gl.createTexture();
	gl.activeTexture(textureUnit); // gl.TEXTURE0, gl.TEXTURE1, etc
	gl.bindTexture(	  gl.TEXTURE_2D, texture);
	gl.texImage2D(	  gl.TEXTURE_2D, /*level*/0, gl.RGBA, w / 4, h, 0,
					  gl.RGBA, gl.FLOAT, texels);

	// clamp to edge to support non-power of two textures
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
	// don't interpolate when getting data from texture
	gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
	gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);

	var sampler = gl.getUniformLocation(program, name);
	gl.uniform1i(sampler, textureUnit - gl.TEXTURE0); // (textureUnit - gl.TEXTURE0)

	return texture;
};

GEMMFloatCalculator.prototype.bindDestinationTexture = function(M, N, dstTex) {
	var gl = this.webgl.context;

	// set canvas and viewport size
	this.webgl.canvas.height = M;
	this.webgl.canvas.width = N;
	gl.viewport(0, 0, N, M);

	// create and bind renderbuffer
	this.renderbuffer = this.renderbuffer || gl.createRenderbuffer();

	gl.bindRenderbuffer(gl.RENDERBUFFER, null);
	gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderbuffer);

	gl.renderbufferStorage(gl.RENDERBUFFER,gl.DEPTH_COMPONENT16, N, M);

	// create and bind framebuffer
	this.framebuffer = this.framebuffer || gl.createFramebuffer();

	gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);

	gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,dstTex,/*level*/0);
	gl.framebufferRenderbuffer(gl.FRAMEBUFFER,gl.DEPTH_ATTACHMENT,gl.RENDERBUFFER,this.renderbuffer);

	return this.framebuffer;
};

/* Calculate the GEMM, with the given data.

  Steps are:

  1. Activate our shader program
  2. Create a texture to hold the input data
  3. Set shader program parameters (based on matrix sizes)
  4. Create a texture to hold the output
  5. Activate calculation with `drawElements`
  6. Read result with `readPixels` and return it

 TODO: signature should look like this:
 ( TRANSA, TRANSB, M, N, K, ALPHA, A, LDA, B, LDB, BETA, C, LDC )
 http://www.math.utah.edu/software/lapack/lapack-blas/dgemm.html
 */
GEMMFloatCalculator.prototype.calculate = function(M, N, K, alpha, A, B, beta, C){

	var gl = this.webgl.context,
		destTexture,
		frameBuffer,
		rawbuffer;

	var h1 = M,
		w1 = K,
		h2 = N, // temp passing in transpose
		w2 = K; // temp passing in transpose

	this.webgl.selectProgram(this.program);

	// pack each matrix into a single RGBA texel array, with the second transposed
	var texels1 = GEMMFloatCalculator.packData(A, h1, w1, false);
	 	texels2 = GEMMFloatCalculator.packData(B, h2, w2, true);

	var mod = (K % 4),
		pad = mod == 0 ? 0 : 4 - mod;

	//console.log("height: {0}, width: {1}".format(this.webgl.canvas.height, this.webgl.canvas.width));
	//console.log("pad: {0}".format(pad));

	// create and bind our input texture using matrix data
	this.bindInputTexture(M, K + pad, texels1, gl.TEXTURE0, GEMMFloatCalculator.TEXTURE_UNIFORM_NAME_1);
	this.bindInputTexture(N, K + pad, texels2, gl.TEXTURE1, GEMMFloatCalculator.TEXTURE_UNIFORM_NAME_2);

	// set the data specific variables in our shader program
	this.bindUniforms(K + pad);
	//this.webgl.bindVertices(this.program);

	// create our destination texture
	destTexture = this.webgl.createDestinationTexture(M, N);
	frameBuffer = this.bindDestinationTexture(M, N, destTexture);

	if( gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)
		throw new Error("Bound framebuffer is not complete.");

	// initiate calculation
	gl.drawElements(gl.TRIANGLES, /*num items*/6, gl.UNSIGNED_SHORT, 0);

	// create destination buffer
	rawbuffer = new ArrayBuffer(M*N*Float32Array.BYTES_PER_ELEMENT);

	// read the result into our buffer, as bytes
	prod = new Uint8Array(rawbuffer);
	gl.readPixels(0, 0, N, M, gl.RGBA, gl.UNSIGNED_BYTE, prod);

	// create and return a view over result bytes as a float array
	return new Float32Array(rawbuffer); // M x N
};

	// set up vars for the shaders
GEMMFloatCalculator.prototype.bindUniforms = function(K) {
	var gl = this.webgl.context;

	// get var locations
	var length	= gl.getUniformLocation(this.program, "K");

	// bind length of shared dimension
	gl.uniform1i(length, K);

};

<commit_msg>update comments to match new method<commit_after>

/* A calculator object for the Float texture based GEMM

	gl - WebGL object

	* uploads and downloads data
	* executes calculation
 */
function GEMMFloatCalculator(webgl){
	this.webgl = webgl;

	// create the webgl shader program for this calculation
	// based on the specific fragment shader for this calculation
	// and the generic pass through shader
	this.program = this.webgl.createProgram(GEMMFloatCalculator.FRAGMENT_SHADER);

}

module.exports = GEMMFloatCalculator;

GEMMFloatCalculator.TEXTURE_UNIFORM_NAME_1 = "A";
GEMMFloatCalculator.TEXTURE_UNIFORM_NAME_2 = "B_t";

GEMMFloatCalculator.FRAGMENT_SHADER = "                                  \n\
// fragment shader that calculates the sum of the passed row and         \n\
// column (texture coord).                                               \n\
// we loop over the row and column and sum the product, using the        \n\
// glsl `dot` function to process four elements at a time                \n\
// product is then rendered to 32-bit IEEE754 floating point in the      \n\
// output RGBA canvas.                                                   \n\
// readPixel is used to read the bytes.                                  \n\
																		 \n\
precision highp float;                                                   \n\
																		 \n\
varying vec2      outTex;	// row, column to calculate                  \n\
uniform sampler2D A;		// padded A                                  \n\
uniform sampler2D B_t;		// padded B transpose                        \n\
uniform int       K;		// interior (shared) dimension               \n\
float delta = 1./float(K);	// space between items in shared dimension   \n\
																		 \n\
// sum row i x col j                                                     \n\
float sumrowcol(float i, float j) {                                      \n\
	float sum = 0.;			// sum for this row/column pair              \n\
	float k = 0.5 * delta;	// position on source texture                \n\
	for (int l=0 ; l<2048 ; ++l) {                                       \n\
		if(l >= K / 4) break;    // stop when we finish the row/column   \n\
																		 \n\
		// retrieve next four elements from each texture                 \n\
		vec4 a_ik = texture2D(  A, vec2(k, i));                          \n\
		vec4 b_kj = texture2D(B_t, vec2(k, j));                          \n\
																		 \n\
		// use `dot` to process four elements at a time                  \n\
		sum += dot(a_ik, b_kj);                                          \n\
		k += (delta * 4.0);      // (k * 4 + 0.5)*delta                  \n\
	}                                                                    \n\
	return sum;                                                          \n\
}                                                                        \n\
																		 \n\
// Render float to bytes according to IEEE 754 Floating Point            \n\
vec4 encodeFloat(float val) {                                            \n\
																		 \n\
	// TODO: correctly handle denormal numbers                           \n\
	// http://www.2ality.com/2012/04/number-encoding.html                \n\
	float a = abs(val);                           // encode absolute value + sign \n\
	float exp = floor(log2(a));                 // number of powers of 2 \n\
	float mant = pow(2.,log2(a)-exp) * pow(2.,23.);  // multiply to fill 24 bits (implied leading 1) \n\
	float mant1 = floor(mant / 256. / 256.);    // first 8 bits of mantissa \n\
	float mant2 = mod(floor(mant / 256.),256.); // second 8 bits         \n\
	float mant3 = mod(mant,256.);               // third 8 bits          \n\
																		 \n\
	highp float sign = 128.-128.*(a/val);			// sign bit is 256 or 0  \n\
	highp float e = (sign+exp+127.)/510.;		// exponent and sign     \n\
	highp float m1 = (mant1-(128.*(1.-mod(exp+127.,2.))))/255.; // handle leading bit \n\
	highp float m2 = (mant2)/255.;				// middle part           \n\
	highp float m3 = (mant3+.5)/255.;			// scale to 0 - 255      \n\
																		 \n\
	return vec4(m3,m2,m1,e);                                             \n\
}                                                                        \n\
																		 \n\
void main(void) {                                                        \n\
																		 \n\
	 // get the implied row and column from .y and .x                    \n\
	 // of passed (output) texel                                         \n\
 	float row = outTex.y;                                                \n\
	float col = outTex.x;                                                \n\
																		 \n\
	// sum row x col for the passed pixel                                \n\
	float sum = sumrowcol(row,col);                                      \n\
																		 \n\
	if (sum == 0.) {                                                     \n\
		gl_FragColor = vec4(0.,0.,0.,0.);                                \n\
		return;                                                          \n\
	}                                                                    \n\
																		 \n\
 	// output an IEEE754 32-bit floating point number 				  	 \n\
	gl_FragColor = encodeFloat(sum);									 \n\
}                                                                        \n\
";

/* Calculate the GEMM, with the given data.

  Steps are:

  1. Activate our shader program
  2. Create a texture to hold the input data
  3. Set shader program parameters (based on matrix sizes)
  4. Create a texture to hold the output
  5. Activate calculation with `drawElements`
  6. Read result with `readPixels` and return it

 TODO: signature should look like this:
 ( TRANSA, TRANSB, M, N, K, ALPHA, A, LDA, B, LDB, BETA, C, LDC )
 http://www.math.utah.edu/software/lapack/lapack-blas/dgemm.html
 */
GEMMFloatCalculator.prototype.calculate = function(M, N, K, alpha, A, B, beta, C){

	var gl = this.webgl.context,
		destTexture,
		frameBuffer,
		rawbuffer;

	var h1 = M,
		w1 = K,
		h2 = N, // temp passing in transpose
		w2 = K; // temp passing in transpose

	this.webgl.selectProgram(this.program);

	// pack each matrix into a single RGBA texel array, with the second transposed
	var texels1 = GEMMFloatCalculator.packData(A, h1, w1, false);
	 	texels2 = GEMMFloatCalculator.packData(B, h2, w2, true);

	var mod = (K % 4),
		pad = mod == 0 ? 0 : 4 - mod;

	//console.log("height: {0}, width: {1}".format(this.webgl.canvas.height, this.webgl.canvas.width));
	//console.log("pad: {0}".format(pad));

	// create and bind our input texture using matrix data
	this.bindInputTexture(M, K + pad, texels1, gl.TEXTURE0, GEMMFloatCalculator.TEXTURE_UNIFORM_NAME_1);
	this.bindInputTexture(N, K + pad, texels2, gl.TEXTURE1, GEMMFloatCalculator.TEXTURE_UNIFORM_NAME_2);

	// set the data specific variables in our shader program
	this.bindUniforms(K + pad);

	// create our destination texture
	destTexture = this.webgl.createDestinationTexture(M, N);
	frameBuffer = this.bindDestinationTexture(M, N, destTexture);

	if( gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)
		throw new Error("Bound framebuffer is not complete.");

	// initiate calculation
	gl.drawElements(gl.TRIANGLES, /*num items*/6, gl.UNSIGNED_SHORT, 0);

	// create destination buffer
	rawbuffer = new ArrayBuffer(M*N*Float32Array.BYTES_PER_ELEMENT);

	// read the result into our buffer, as bytes
	prod = new Uint8Array(rawbuffer);
	gl.readPixels(0, 0, N, M, gl.RGBA, gl.UNSIGNED_BYTE, prod);

	// create and return a view over result bytes as a float array
	return new Float32Array(rawbuffer); // M x N
};

	// set up vars for the shaders
GEMMFloatCalculator.prototype.bindUniforms = function(K) {
	var gl = this.webgl.context;

	// get var locations
	var length	= gl.getUniformLocation(this.program, "K");

	// bind length of shared dimension
	gl.uniform1i(length, K);

};

/* Pack the given matrix data into texel layout for use by texture shader.
   This layout places consecutive elements of the input data into seperate
   channels, padding where necessary to fill out the final texel in a column.
 */
GEMMFloatCalculator.packData = function(data, r, c, transpose) {

	var CHANNELS_PER_TEXEL = 4; // RGBA: four channels, one per color

	var mod = (c % CHANNELS_PER_TEXEL),
		pad = CHANNELS_PER_TEXEL - mod;

	if (mod === 0) {
		// special case if column count is a multiple of number of channels
		return data;
	}

	// dimensions
	var dst = 0,
		src = 0,
		p = 0;

	var texelcount = r*(c + pad);
	// create Float32Array to hold padded texel data
	var texels = new Float32Array(texelcount);

	// pad each row to multiple of four, with zeros
	for(dst = 0, src = 0; dst < texelcount; dst++, src++){

		if(src != 0 && src % c == 0){
			// reached the end of a source column, pad with zeros
			for(p = 0; p < pad; p++){
				texels[dst + p] = 0.0;
			}
			dst += pad;
		}

		// copy the real data
		texels[dst] = data[src];

	}

	return texels;
};

/* Create a texture from the given texel data and bind it to our shader program.
  must set up shader first
*/
GEMMFloatCalculator.prototype.bindInputTexture = function(h, w, texels, textureUnit, name){
	var gl = this.webgl.context,
		program = this.program;

	// create the texture from our floats
	var texture = gl.createTexture();
	gl.activeTexture(textureUnit); // gl.TEXTURE0, gl.TEXTURE1, etc
	gl.bindTexture(	  gl.TEXTURE_2D, texture);
	gl.texImage2D(	  gl.TEXTURE_2D, /*level*/0, gl.RGBA, w / 4, h, 0,
					  gl.RGBA, gl.FLOAT, texels);

	// clamp to edge to support non-power of two textures
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
	// don't interpolate when getting data from texture
	gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
	gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);

	var sampler = gl.getUniformLocation(program, name);
	gl.uniform1i(sampler, textureUnit - gl.TEXTURE0); // (textureUnit - gl.TEXTURE0)

	return texture;
};

GEMMFloatCalculator.prototype.bindDestinationTexture = function(M, N, dstTex) {
	var gl = this.webgl.context;

	// set canvas and viewport size
	this.webgl.canvas.height = M;
	this.webgl.canvas.width = N;
	gl.viewport(0, 0, N, M);

	// create and bind renderbuffer
	this.renderbuffer = this.renderbuffer || gl.createRenderbuffer();

	gl.bindRenderbuffer(gl.RENDERBUFFER, null);
	gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderbuffer);

	gl.renderbufferStorage(gl.RENDERBUFFER,gl.DEPTH_COMPONENT16, N, M);

	// create and bind framebuffer
	this.framebuffer = this.framebuffer || gl.createFramebuffer();

	gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);

	gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,dstTex,/*level*/0);
	gl.framebufferRenderbuffer(gl.FRAMEBUFFER,gl.DEPTH_ATTACHMENT,gl.RENDERBUFFER,this.renderbuffer);

	return this.framebuffer;
};
<|endoftext|>


<commit_before>// Latte supports Mocha syntax for now, but we'd like to evolve.
// It needs to support two different modes: headless and debug
// * headless runs in the background, and is optimized for speed.
// * debug is optimized for an engineer with devtools open.
(function() {
  window.Latte = {}

  // create the root describe block
  var current = Latte.root = {
    children: [], tests: [],
    before: [], beforeEach: [], after: [], afterEach: [], helpers: {}
  }

  var currentContext = null // The context of the currently running test.
  var abort = false // stop tests mid-run. Usually when code changes
  var currentTimeout = null

  window.describe = function(name, fn) {
    var previous = current
    current = {
      parent: previous, name: name, children: [], tests: [],
      fullName: [previous.fullName, name].filter(s => !!s).join(' '),
      before: [], beforeEach: [], after: [], afterEach: [], helpers: {},
      timeout: (val) => {} // TODO
    }

    // Replace ourselves in the heirarchy. This happens when a test file is hot-reloaded.
    // TODO: if the name changes, this breaks. Maybe use filename instead?
    let oldIndex = previous.children.findIndex(d => d.name == name)
    if (~oldIndex)
      previous.children[oldIndex] = current
    else
      previous.children.push(current)

    fn.call(current)
    current = previous
  }

  window.context = window.describe
  describe.skip = function(name) {}
  describe.only = function(name) {} // TODO
  window.timeout = function(val) {} // TODO
  window.beforeEach = function(fn) { current.beforeEach.push({fn, stack: getStack()}) }
  window.afterEach = function(fn) { current.afterEach.push({fn, stack: getStack()}) }
  window.before = function(fn) { current.before.push({fn, stack: getStack()}) }
  window.after = function(fn) { current.after.push({fn, stack: getStack()}) }

  // Helpers add methods to the current scope that aid writing clean tests.
  // For example, you might have a `login` or `goOffline` helper that you can call at the top of a
  // test to get things in the right state. Helpers are usually preferred over `beforeEach`, because
  // having setup code in the test makes it easier to understand.
  //
  // Helpers can be defined as a single function: `helper('login', function() {...})`
  // Or as a group: `helper('Factory', {user: function() {...}, hats: function() {...}})`
  // Groups are a great way to organize related helpers.
  window.helper = function(name, fnOrGroup) { current.helpers[name || 'default'] = fnOrGroup }

  window.it = function(name, fn) {
    fullName = current.fullName + ' ' + name
    current.tests.push({fn, name, fullName, suite: current, stack: getStack()})
  }
  it.skip = function() {}
  it.only = function() {}

  function getStack() {
    return new Error().stack.split('\n')[3].trim()
  }

  // Cancel any remaining tests and clean up.
  // TODO: this should let the current test finish, though that's tricky in debug mode (where uncaught exceptions are the norm)
  Latte.abort = async function latteClean() {
    abort = true

    if (currentContext) {
      await changeSuite(currentContext, null)
      currentContext = null
    }
  }

  // Get all tests (optionally of a specific suite) flattened into an array
  Latte.flatten = function latteFlatten(suite) {
    suite = suite || Latte.root

    var allTests = [], queue = [Latte.root]
    while (step = queue.pop()) {
      allTests = allTests.concat(step.tests)
      queue = queue.concat(step.children)
    }
    return allTests
  }

  Latte.run = async function latteRun(tests) {
    abort = false

    for (test of tests) {
      if (!test.fn) continue
      console.log(`${test.fullName}`)

      // apply afterEach from the inside out
      if (currentContext && Latte.mode == 'debug')
        await applyCallbacks('afterEach', currentContext)

      // each test gets a context that inherits from the suite-level context.
      // This isolates each test but lets us do some setup at the suite-level for speed.
      previousSuiteContext = currentContext && Object.getPrototypeOf(currentContext)

      if (Latte.mode == 'headless') {
        try { suiteContext = await changeSuite(previousSuiteContext, test.suite) }
        catch(e) { fatalError(e) }
      }

      if (Latte.mode == 'debug') {
        suiteContext = await changeSuite(previousSuiteContext, test.suite)
      }

      currentContext = Object.create(suiteContext)

      if (Latte.mode == 'headless') {
        try {
          await applyCallbacks('beforeEach', currentContext, {topDown: true})
          await runWithTimeout(test, currentContext)
          Latte.onTest(test)
        }
        catch (e) {
          clearTimeout(currentTimeout)
          Latte.onTest(test, e || 'undefined error')
        }

        try { await applyCallbacks('afterEach', currentContext) }
        catch (e) { fatalError(e) }
      }

      if (Latte.mode == 'debug') {
        await applyCallbacks('beforeEach', currentContext, {topDown: true})
        await runWithTimeout(test, currentContext)
      }

      if (abort) break
    }

    console.log('Done!')
  }

  function fatalError(e) {
    console.error(e)
    window.location.reload()
    throw e
  }

  // Change our context from one suite to another by running the minimal set of before/after callbacks.
  // Any common ancestors between the two suites don't need to run.
  async function changeSuite(context, nextSuite) {
    let currentSuite = context && context._suite

    // short circuit when the suite doesn't actually change
    if (context && context._suite === nextSuite)
      return context

    // figure out what the common ancestor is between both suites. If either suite is null
    // (happens at beginning and end of run) then commonAncestor will be undefined, and our
    // slice calls below will yield the full lineage.
    let currLineage = lineage(currentSuite)
    let nextLineage = lineage(nextSuite)
    let commonAncestor = currLineage.filter(x => nextLineage.indexOf(x) >= 0)[0]

    // walk the lineage up to (but not including) the common ancestor, running after callbacks
    let currTop = currLineage.indexOf(commonAncestor)
    currTop = currTop == -1 ? undefined : currTop
    let chain = currLineage.slice(0, currTop)
    for (suite of chain) {
      for (cb of suite.after)
        await runWithTimeout(cb, context)
      context = Object.getPrototypeOf(context)
    }

    // now walk down the lineage from right below the common ancestor to the new suite, running before callbacks
    let nextTop = nextLineage.indexOf(commonAncestor)
    nextTop = nextTop == -1 ? undefined : nextTop
    chain = nextLineage.slice(0, nextTop).reverse()
    for (suite of chain) {
      context = Object.create(context)
      context._suite = suite
      context.timeout = function() {}
      attachHelpers(suite, context)
      for (cb of suite.before)
        await runWithTimeout(cb, context)
    }

    return context
  }

  // Run user code with a timeout
  async function runWithTimeout(cbOrTest, context) {
    let timeoutPromise = new Promise((res, rej) => {
      currentTimeout = setTimeout(() => {
        if (Latte.mode == 'debug') {
          console.error('Timeout', cbOrTest.stack)
          abort = true
        }
        else
          rej(`Timeout ${cbOrTest.stack}`)
      }, 2000)
    })

    return Promise.race([timeoutPromise, cbOrTest.fn.call(context)])
      .then(() => clearTimeout(currentTimeout))
  }

  // run all the callbacks (before, after, beforeEach, or afterEach) for each suite.
  async function applyCallbacks(type, context, {topDown}={}) {
    let suites = lineage(context._suite)
    if (topDown) suites = suites.reverse()

    for (suite of suites)
      for (cb of suite[type])
        await runWithTimeout(cb, context)
  }

  // Get all the suites between the current one and the root.
  function lineage(suite) {
    if (!suite) return []

    let arr = []
    while (suite) {
      arr.push(suite)
      suite = suite.parent
    }
    return arr
  }

  function attachHelpers(suite, context) {
    for (name in suite.helpers) {
      let obj = suite.helpers[name]

      if (typeof(obj) == 'function') {
        context[name] = obj
        continue
      }

      let dest = name == 'default' ? context : (context[name] = {})
      for (prop of Object.keys(obj)) {
        let val = obj[prop]
        let isFunction = typeof(val) == 'function'
        Object.defineProperty(dest, prop, { get: () => isFunction ? val.bind(currentContext) : val})
      }
    }
  }

})()
<commit_msg>Abort should also run afterEach<commit_after>// Latte supports Mocha syntax for now, but we'd like to evolve.
// It needs to support two different modes: headless and debug
// * headless runs in the background, and is optimized for speed.
// * debug is optimized for an engineer with devtools open.
(function() {
  window.Latte = {}

  // create the root describe block
  var current = Latte.root = {
    children: [], tests: [],
    before: [], beforeEach: [], after: [], afterEach: [], helpers: {}
  }

  var currentContext = null // The context of the currently running test.
  var abort = false // stop tests mid-run. Usually when code changes
  var currentTimeout = null

  window.describe = function(name, fn) {
    var previous = current
    current = {
      parent: previous, name: name, children: [], tests: [],
      fullName: [previous.fullName, name].filter(s => !!s).join(' '),
      before: [], beforeEach: [], after: [], afterEach: [], helpers: {},
      timeout: (val) => {} // TODO
    }

    // Replace ourselves in the heirarchy. This happens when a test file is hot-reloaded.
    // TODO: if the name changes, this breaks. Maybe use filename instead?
    let oldIndex = previous.children.findIndex(d => d.name == name)
    if (~oldIndex)
      previous.children[oldIndex] = current
    else
      previous.children.push(current)

    fn.call(current)
    current = previous
  }

  window.context = window.describe
  describe.skip = function(name) {}
  describe.only = function(name) {} // TODO
  window.timeout = function(val) {} // TODO
  window.beforeEach = function(fn) { current.beforeEach.push({fn, stack: getStack()}) }
  window.afterEach = function(fn) { current.afterEach.push({fn, stack: getStack()}) }
  window.before = function(fn) { current.before.push({fn, stack: getStack()}) }
  window.after = function(fn) { current.after.push({fn, stack: getStack()}) }

  // Helpers add methods to the current scope that aid writing clean tests.
  // For example, you might have a `login` or `goOffline` helper that you can call at the top of a
  // test to get things in the right state. Helpers are usually preferred over `beforeEach`, because
  // having setup code in the test makes it easier to understand.
  //
  // Helpers can be defined as a single function: `helper('login', function() {...})`
  // Or as a group: `helper('Factory', {user: function() {...}, hats: function() {...}})`
  // Groups are a great way to organize related helpers.
  window.helper = function(name, fnOrGroup) { current.helpers[name || 'default'] = fnOrGroup }

  window.it = function(name, fn) {
    fullName = current.fullName + ' ' + name
    current.tests.push({fn, name, fullName, suite: current, stack: getStack()})
  }
  it.skip = function() {}
  it.only = function() {}

  function getStack() {
    return new Error().stack.split('\n')[3].trim()
  }

  // Cancel any remaining tests and clean up.
  // TODO: this should let the current test finish, though that's tricky in debug mode (where uncaught exceptions are the norm)
  Latte.abort = async function latteClean() {
    abort = true

    if (currentContext) {
      await applyCallbacks('afterEach', currentContext)
      await changeSuite(currentContext, null)
      currentContext = null
    }
  }

  // Get all tests (optionally of a specific suite) flattened into an array
  Latte.flatten = function latteFlatten(suite) {
    suite = suite || Latte.root

    var allTests = [], queue = [Latte.root]
    while (step = queue.pop()) {
      allTests = allTests.concat(step.tests)
      queue = queue.concat(step.children)
    }
    return allTests
  }

  Latte.run = async function latteRun(tests) {
    abort = false

    for (test of tests) {
      if (!test.fn) continue
      console.log(`${test.fullName}`)

      // apply afterEach from the inside out
      if (currentContext && Latte.mode == 'debug')
        await applyCallbacks('afterEach', currentContext)

      // each test gets a context that inherits from the suite-level context.
      // This isolates each test but lets us do some setup at the suite-level for speed.
      previousSuiteContext = currentContext && Object.getPrototypeOf(currentContext)

      if (Latte.mode == 'headless') {
        try { suiteContext = await changeSuite(previousSuiteContext, test.suite) }
        catch(e) { fatalError(e) }
      }

      if (Latte.mode == 'debug') {
        suiteContext = await changeSuite(previousSuiteContext, test.suite)
      }

      currentContext = Object.create(suiteContext)

      if (Latte.mode == 'headless') {
        try {
          await applyCallbacks('beforeEach', currentContext, {topDown: true})
          await runWithTimeout(test, currentContext)
          Latte.onTest(test)
        }
        catch (e) {
          clearTimeout(currentTimeout)
          Latte.onTest(test, e || 'undefined error')
        }

        try { await applyCallbacks('afterEach', currentContext) }
        catch (e) { fatalError(e) }
      }

      if (Latte.mode == 'debug') {
        await applyCallbacks('beforeEach', currentContext, {topDown: true})
        await runWithTimeout(test, currentContext)
      }

      if (abort) break
    }

    console.log('Done!')
  }

  function fatalError(e) {
    console.error(e)
    window.location.reload()
    throw e
  }

  // Change our context from one suite to another by running the minimal set of before/after callbacks.
  // Any common ancestors between the two suites don't need to run.
  async function changeSuite(context, nextSuite) {
    let currentSuite = context && context._suite

    // short circuit when the suite doesn't actually change
    if (context && context._suite === nextSuite)
      return context

    // figure out what the common ancestor is between both suites. If either suite is null
    // (happens at beginning and end of run) then commonAncestor will be undefined, and our
    // slice calls below will yield the full lineage.
    let currLineage = lineage(currentSuite)
    let nextLineage = lineage(nextSuite)
    let commonAncestor = currLineage.filter(x => nextLineage.indexOf(x) >= 0)[0]

    // walk the lineage up to (but not including) the common ancestor, running after callbacks
    let currTop = currLineage.indexOf(commonAncestor)
    currTop = currTop == -1 ? undefined : currTop
    let chain = currLineage.slice(0, currTop)
    for (suite of chain) {
      for (cb of suite.after)
        await runWithTimeout(cb, context)
      context = Object.getPrototypeOf(context)
    }

    // now walk down the lineage from right below the common ancestor to the new suite, running before callbacks
    let nextTop = nextLineage.indexOf(commonAncestor)
    nextTop = nextTop == -1 ? undefined : nextTop
    chain = nextLineage.slice(0, nextTop).reverse()
    for (suite of chain) {
      context = Object.create(context)
      context._suite = suite
      context.timeout = function() {}
      attachHelpers(suite, context)
      for (cb of suite.before)
        await runWithTimeout(cb, context)
    }

    return context
  }

  // Run user code with a timeout
  async function runWithTimeout(cbOrTest, context) {
    let timeoutPromise = new Promise((res, rej) => {
      currentTimeout = setTimeout(() => {
        if (Latte.mode == 'debug') {
          console.error('Timeout', cbOrTest.stack)
          abort = true
        }
        else
          rej(`Timeout ${cbOrTest.stack}`)
      }, 2000)
    })

    return Promise.race([timeoutPromise, cbOrTest.fn.call(context)])
      .then(() => clearTimeout(currentTimeout))
  }

  // run all the callbacks (before, after, beforeEach, or afterEach) for each suite.
  async function applyCallbacks(type, context, {topDown}={}) {
    let suites = lineage(context._suite)
    if (topDown) suites = suites.reverse()

    for (suite of suites)
      for (cb of suite[type])
        await runWithTimeout(cb, context)
  }

  // Get all the suites between the current one and the root.
  function lineage(suite) {
    if (!suite) return []

    let arr = []
    while (suite) {
      arr.push(suite)
      suite = suite.parent
    }
    return arr
  }

  function attachHelpers(suite, context) {
    for (name in suite.helpers) {
      let obj = suite.helpers[name]

      if (typeof(obj) == 'function') {
        context[name] = obj
        continue
      }

      let dest = name == 'default' ? context : (context[name] = {})
      for (prop of Object.keys(obj)) {
        let val = obj[prop]
        let isFunction = typeof(val) == 'function'
        Object.defineProperty(dest, prop, { get: () => isFunction ? val.bind(currentContext) : val})
      }
    }
  }

})()
<|endoftext|>


<commit_before>/*
 * nconf.js: Top-level include for the nconf module
 *
 * (C) 2011, Charlie Robbins and the Contributors.
 *
 */

var fs = require('fs'),
    async = require('async'),
    common = require('./nconf/common'),
    Provider = require('./nconf/provider').Provider;

//
// `nconf` is by default an instance of `nconf.Provider`.
//
var nconf = module.exports = new Provider();

//
// Expose the version from the package.json
//
nconf.version = require('../package.json').version;

//
// Setup all stores as lazy-loaded getters.
//
fs.readdirSync(__dirname + '/nconf/stores').forEach(function (file) {
  var store = file.replace('.js', ''),
      name  = common.capitalize(store);

  nconf.__defineGetter__(name, function () {
    return require('./nconf/stores/' + store)[name];
  });
});

//
// Expose the various components included with nconf
//
nconf.key           = common.key;
nconf.path          = common.path;
nconf.loadFiles     = common.loadFiles;
nconf.loadFilesSync = common.loadFilesSync;
nconf.formats       = require('./nconf/formats');
nconf.Provider      = Provider;

<commit_msg>enable use with webpack by removing unnecessary fs call (#252)<commit_after>/*
 * nconf.js: Top-level include for the nconf module
 *
 * (C) 2011, Charlie Robbins and the Contributors.
 *
 */

var async = require('async'),
    common = require('./nconf/common'),
    Provider = require('./nconf/provider').Provider;

//
// `nconf` is by default an instance of `nconf.Provider`.
//
var nconf = module.exports = new Provider();

//
// Expose the version from the package.json
//
nconf.version = require('../package.json').version;

//
// Setup all stores as lazy-loaded getters.
//
['argv', 'env', 'file', 'literal', 'memory'].forEach(function (store) {
    var name = common.capitalize(store);

    nconf.__defineGetter__(name, function () {
        return require('./nconf/stores/' + store)[name];
    });
});

//
// Expose the various components included with nconf
//
nconf.key           = common.key;
nconf.path          = common.path;
nconf.loadFiles     = common.loadFiles;
nconf.loadFilesSync = common.loadFilesSync;
nconf.formats       = require('./nconf/formats');
nconf.Provider      = Provider;
<|endoftext|>


<commit_before>(function() {

var isMac = (Browser.Platform.name == 'mac'),
	controlRE = /control|ctrl/g,
	macControlToMeta = function(text) {
		return isMac ? text.replace(controlRE, 'meta') : text;
	};

FlightDeck.Keyboard = new Class({

	Extends: Keyboard,

	addEvent: function(type, fn, internal) {
		return this.parent(macControlToMeta(type), fn, internal);
	},

	removeEvent: function(type, fn) {
		return this.parent(macControlToMeta(type), fn);
	},

	addShortcut: function(name, shortcut) {
		if (shortcut.keys) {
			shortcut.keys = macControlToMeta(shortcut.keys);
		}
		return this.parent(name, shortcut);
	}

});

Event.Keys.meta = 224;

})();<commit_msg>stop converting control to cmd for Mac<commit_after>(function() {

FlightDeck.Keyboard = new Class({

	Extends: Keyboard

});

Event.Keys.meta = 224;

})();<|endoftext|>