<commit_before>(function() {
"use strict";

/* global describe */
/* global it */
/* jshint maxlen: 100 */

var Assert = require('chai').assert;
var Stxt = require('../src/stxt.js');
var Hash = require('../src/hash.js');

describe('Basic', function(){
    it('testsuite functions', function(){
        Assert.ok(Stxt.return_true());
    });
});

describe('Hash', function(){
    it('passes 3 sha256 test vectors', function(){
        var sha256_tvecs = [
            // Nb: the test input is JSON.stringify(input), so 'a' is actually '"a"'.
            ['a', 'ac8d8342bbb2362d13f0a559a3621bb407011368895164b628a54f7fc33fc43c'],
            ['dog', 'a52e833634f22ad98e3ff8814fa79b59d3e5645dcf7cca077c606402c0d2d4f3'],
            ['cat', 'e852be0aa593e30e99f250e7b39a3741d00bb1b86796303bded2c9aa5d7fcc08']
        ];
        sha256_tvecs.forEach(function(pair) {
            Assert.equal(Hash.hash(pair[0]), pair[1]);
        });
    });

    it('passes 3 sha256-hmac test vectors', function(){
        var sha256_hmac_tvecs = [
            // Nb: the test input is JSON.stringify(input), so 'a' is actually '"a"'.
            ['a', 'ff35f5eb3de1ed78d45a5cbd64c3907017846831f34c4a5c760786a730a4ece4'],
            ['dog', '883be5dd16f36bb8f42859c2bc74825723ec9e3612405361589541773bcc1922'],
            ['cat', 'eda926345c0cfd718c43a645403cd918f55b5b39c4d9687ecf91ed75bc38ef66']
        ];
        sha256_hmac_tvecs.forEach(function(pair) {
            Assert.equal(Hash.hmac("key", pair[0]), pair[1]);
        });
    });

});

describe('Curve25519', function() {
    it('passes the first 10 donna test vectors', function() {
        var donna_tvecs = [
            ["0300000000000000000000000000000000000000000000000000000000000000",
             "0900000000000000000000000000000000000000000000000000000000000000",
             "2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74"],
            ["0500000000000000000000000000000000000000000000000000000000000000",
             "2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74",
             "93fea2a7c1aeb62cfd6452ff5badae8bdffcbd7196dc910c89944006d85dbb68"],
            ["0500000000000000000000000000000000000000000000000000000000000000",
             "0900000000000000000000000000000000000000000000000000000000000000",
             "2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74"],
            ["0300000000000000000000000000000000000000000000000000000000000000",
             "2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74",
             "93fea2a7c1aeb62cfd6452ff5badae8bdffcbd7196dc910c89944006d85dbb68"],
            ["2ce57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74",
             "9afea2a7c1aeb62cfd6452ff5badae8bdffcbd7196dc910c89944006d85dbb68",
             "2f166b2aa2bb677827572fa3606e9d143523d9c135cdf2403af28a1bf246a10a"],
            ["2ae57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74",
             "2f166b2aa2bb677827572fa3606e9d143523d9c135cdf2403af28a1bf246a10a",
             "fd2fbba00d997278a75827810b4efc5c1259c731cb6a7185deed26fe6413f005"],
            ["2ae57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74",
             "9afea2a7c1aeb62cfd6452ff5badae8bdffcbd7196dc910c89944006d85dbb68",
             "2f166b2aa2bb677827572fa3606e9d143523d9c135cdf2403af28a1bf246a10a"],
            ["2ce57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74",
             "2f166b2aa2bb677827572fa3606e9d143523d9c135cdf2403af28a1bf246a10a",
             "fd2fbba00d997278a75827810b4efc5c1259c731cb6a7185deed26fe6413f005"],
            ["03f31689e576053b327ff50f3fd5b41305dc2f459a093d82d7621344aa8d9a7e",
             "67d11907cc37c4545a3c757e50e352d7cda57a405db6e089577966f8bc4e4b6d",
             "593251e741a7a6b24be916b624d43f3b20a3a2244c8003722e9765cff71ca857"],
            ["05f31689e576053b327ff50f3fd5b41305dc2f459a093d82d7621344aa8d9a7e",
             "593251e741a7a6b24be916b624d43f3b20a3a2244c8003722e9765cff71ca857",
             "fd52d04fe48fe65107680ee175cbfab3fb526e7412bc95806a44c6f88676f877"],
        ];
        donna_tvecs.forEach(function(triple) {
            var c255 = Stxt.curve25519;
            var r = c255.crypto_scalarmult(c255.from_hex(triple[0]),
                                           c255.from_hex(triple[1]));
            Assert.equal(c255.to_hex(r), triple[2]);
        });
    });
});

})();
<commit_msg>test tags<commit_after>(function() {
"use strict";

/* global describe */
/* global it */
/* jshint maxlen: 100 */

var Assert = require('chai').assert;
var Stxt = require('../src/stxt.js');

var Hash = Stxt.Hash;
var Tag = Stxt.Tag;

describe('Basic', function(){
    it('testsuite functions', function(){
        Assert.ok(Stxt.return_true());
    });
});

describe('Hash (sha256)', function(){
    it('passes 3 sha256 test vectors', function(){
        var sha256_tvecs = [
            // Nb: the test input is JSON.stringify(input), so 'a' is actually '"a"'.
            ['a', 'ac8d8342bbb2362d13f0a559a3621bb407011368895164b628a54f7fc33fc43c'],
            ['dog', 'a52e833634f22ad98e3ff8814fa79b59d3e5645dcf7cca077c606402c0d2d4f3'],
            ['cat', 'e852be0aa593e30e99f250e7b39a3741d00bb1b86796303bded2c9aa5d7fcc08']
        ];
        sha256_tvecs.forEach(function(pair) {
            Assert.equal(Hash.hash(pair[0]), pair[1]);
        });
    });

    it('passes 3 sha256-hmac test vectors', function(){
        var sha256_hmac_tvecs = [
            // Nb: the test input is JSON.stringify(input), so 'a' is actually '"a"'.
            ['a', 'ff35f5eb3de1ed78d45a5cbd64c3907017846831f34c4a5c760786a730a4ece4'],
            ['dog', '883be5dd16f36bb8f42859c2bc74825723ec9e3612405361589541773bcc1922'],
            ['cat', 'eda926345c0cfd718c43a645403cd918f55b5b39c4d9687ecf91ed75bc38ef66']
        ];
        sha256_hmac_tvecs.forEach(function(pair) {
            Assert.equal(Hash.hmac("key", pair[0]), pair[1]);
        });
    });
});

describe('Tag', function(){
    var bob = Tag.new_user('bob');
    it('generates tags with correct nick and kind', function(){
        Assert.equal(bob.kind, 'u');
        Assert.equal(bob.nick, 'bob');
    });
    it('can re-parse its own stringification', function(){
        Assert.deepEqual(bob, Tag.parse(bob.toString()));
    });
});

describe('Curve25519', function() {
    it('passes the first 10 donna test vectors', function() {
        var donna_tvecs = [
            ["0300000000000000000000000000000000000000000000000000000000000000",
             "0900000000000000000000000000000000000000000000000000000000000000",
             "2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74"],
            ["0500000000000000000000000000000000000000000000000000000000000000",
             "2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74",
             "93fea2a7c1aeb62cfd6452ff5badae8bdffcbd7196dc910c89944006d85dbb68"],
            ["0500000000000000000000000000000000000000000000000000000000000000",
             "0900000000000000000000000000000000000000000000000000000000000000",
             "2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74"],
            ["0300000000000000000000000000000000000000000000000000000000000000",
             "2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74",
             "93fea2a7c1aeb62cfd6452ff5badae8bdffcbd7196dc910c89944006d85dbb68"],
            ["2ce57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74",
             "9afea2a7c1aeb62cfd6452ff5badae8bdffcbd7196dc910c89944006d85dbb68",
             "2f166b2aa2bb677827572fa3606e9d143523d9c135cdf2403af28a1bf246a10a"],
            ["2ae57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74",
             "2f166b2aa2bb677827572fa3606e9d143523d9c135cdf2403af28a1bf246a10a",
             "fd2fbba00d997278a75827810b4efc5c1259c731cb6a7185deed26fe6413f005"],
            ["2ae57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74",
             "9afea2a7c1aeb62cfd6452ff5badae8bdffcbd7196dc910c89944006d85dbb68",
             "2f166b2aa2bb677827572fa3606e9d143523d9c135cdf2403af28a1bf246a10a"],
            ["2ce57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74",
             "2f166b2aa2bb677827572fa3606e9d143523d9c135cdf2403af28a1bf246a10a",
             "fd2fbba00d997278a75827810b4efc5c1259c731cb6a7185deed26fe6413f005"],
            ["03f31689e576053b327ff50f3fd5b41305dc2f459a093d82d7621344aa8d9a7e",
             "67d11907cc37c4545a3c757e50e352d7cda57a405db6e089577966f8bc4e4b6d",
             "593251e741a7a6b24be916b624d43f3b20a3a2244c8003722e9765cff71ca857"],
            ["05f31689e576053b327ff50f3fd5b41305dc2f459a093d82d7621344aa8d9a7e",
             "593251e741a7a6b24be916b624d43f3b20a3a2244c8003722e9765cff71ca857",
             "fd52d04fe48fe65107680ee175cbfab3fb526e7412bc95806a44c6f88676f877"],
        ];
        donna_tvecs.forEach(function(triple) {
            var c255 = Stxt.curve25519;
            var r = c255.crypto_scalarmult(c255.from_hex(triple[0]),
                                           c255.from_hex(triple[1]));
            Assert.equal(c255.to_hex(r), triple[2]);
        });
    });
});

})();
<|endoftext|>


<commit_before>'use strict';

const DEFAULT_EPUB_TITLE = '*';

const searchIndexSource = require('search-index'),
    Q = require('q'),
    searchIndex = Q.denodeify(searchIndexSource),
    colors = require('colors'),
    fs = require('extfs'),
    _ = require('lodash'),
    preparer = require('./Preparer.js'),
    cfi = require('./CFI.js'),
    constants = require("./Constants");

module.exports = function (options) {

    var SearchEngine = {};

    var defaultOption = {'indexPath': constants.DATA_FOLDER};
    var options = _.isEmpty(options) ? defaultOption : options;

    SearchEngine.indexing = function (pathToEpubs) {

        if (fs.isEmptySync(pathToEpubs)) {
            return Q.reject(new Error('Can`t index empty folder: ' + pathToEpubs));
        }
        console.log("\n\n\n******************************************************\n");
        console.log("Start normalize epub content\n\n".yellow);

        // normalize the directory path
        pathToEpubs = path.normalize(pathToEpubs);

        return preparer.normalize(pathToEpubs, options)
            .then(function (dataSet) {
                console.log("\n******************************************************\n");
                console.log("Ready with normalize epub content\n\n".yellow);

                if (dataSet.length > 0)
                    console.log("Start indexing epub-data.");
                else {
                    console.log("DONE! Nothing to do, epubs already indexed.\n\n");
                    return;
                }

                return SearchEngine.add(dataSet);
            });
    };

    SearchEngine.add = function (jsonDoc) {

        var ids = jsonDoc.FirstSpineItemsId;
        delete jsonDoc.FirstSpineItemsId;

        return SearchEngine._add(jsonDoc, getIndexOptions());
    };

    SearchEngine.search = function (searchFor, bookTitle) {

        bookTitle = bookTitle || DEFAULT_EPUB_TITLE; // * if bookTitle undefined return all hits

        var q = {};
        q.query =
            {
                AND: {
                    'epubTitle': [preparer.normalizeEpupTitle(bookTitle)],
                    'body': [searchFor]
                }
            };

        return SearchEngine.query(q, searchFor);
    };

    SearchEngine.query = function (query, searchFor) {

        return SearchEngine._search(query)
            .then(function (result) {
                const hits = [];

                if (!result.hits) {
                    return hits;
                }

                result.hits.forEach(function (hit) {
                    const document = hit.document,
                        idData = document.id.split(':');

                    document.id = idData[0];

                    const cfiList = cfi.generate({
                        "searchFor": searchFor,
                        "spineItemPath": document.spineItemPath,
                        "baseCfi": document.baseCfi
                    });

                    if (cfiList.length > 0) {
                        document.cfis = cfiList;
                        delete document['*'];
                        delete document.spineItemPath;

                        hits.push(document);
                    }

                });
                return hits;
            });
    };

    SearchEngine.match = function (beginsWith, epubTitle) {

        if (!_.isString(epubTitle) && !_.isNull(epubTitle))
            console.error('epubTitle should be null or type string');

        var epubTitle = epubTitle || DEFAULT_EPUB_TITLE;

        return SearchEngine._match({beginsWith: beginsWith, type: 'ID'})
            .then(function (matches) {
                return filterMatches(matches, epubTitle);
            });
    };

    SearchEngine.empty = function () {
        return SearchEngine._empty();
    };

    SearchEngine.close = function () {
        return SearchEngine._close();
    };

// private
    function getIndexOptions() {
        return {
            fieldOptions: [
                {fieldName: 'epubTitle', searchable: false, store: true},
                {fieldName: 'spineItemPath', searchable: false, store: true},
                {fieldName: 'href', searchable: false, store: true},
                {fieldName: 'baseCfi', searchable: false, store: true},
                {fieldName: 'id', searchable: false, store: true},
                {fieldName: 'filename', searchable: true, store: true},
                {fieldName: 'title', searchable: true, store: false},
                {fieldName: 'body', searchable: true, store: false}
            ]
        };
    }

    function filterMatches(matches, epubTitle) {

        return matches
            .map(function (match) {

                if (epubTitle === '*') {
                    // if epubTitle undefined return all matches
                    return match[0];
                } else {
                    var titles = match[1].map(function (id) {
                        // id = spineitemid:epubtitle
                        return id.split(':')[1]
                    });
                    return _.include(titles, epubTitle) ? match[0] : '';
                }
            })
            .filter(Boolean); // filter ["", "", ""] -> []
    }

    return searchIndex(options)
        .then(function (si) {
            SearchEngine.si = si;
            SearchEngine._search = Q.nbind(si.search, si);
            SearchEngine._close = Q.nbind(si.close, si);
            SearchEngine._empty = Q.nbind(si.empty, si);
            SearchEngine._match = Q.nbind(si.match, si);
            SearchEngine._add = Q.nbind(si.add, si);
            return SearchEngine;
        });
};

/* require('search-index')({
 nGramLength: [1, 2, 3],   // allow phrase search on phrases of length 1, 2 and 3 words
 stopwords: []             // dont strip out stopwords
 }, function(err, si) {
 si.add([
 {date: 1464122926, text: 'some text'},
 {date: 1464122916, text: 'some another text'}
 ], function (err) {
 if (! err) {
 var q = {};
 q.query = { AND: [{'*': ['text']}] };
 si.search(q, function (err, results) {
 console.log(results);
 })
 }
 })
 })
 1. Use nGramLength to activate phrase search

 2. Set stopwords to []. By default, search-index will remove common english words like 'some' and 'another',
 so you need to empty your stopword list to allow them to
 be searchable (you could also remove them from the stopword list individually) */<commit_msg>- added missing module dependency<commit_after>'use strict';

const DEFAULT_EPUB_TITLE = '*';

const searchIndexSource = require('search-index'),
    Q = require('q'),
    searchIndex = Q.denodeify(searchIndexSource),
    colors = require('colors'),
    fs = require('extfs'),
    _ = require('lodash'),
    preparer = require('./Preparer.js'),
    cfi = require('./CFI.js'),
    path = require('path'),
    constants = require("./Constants");

module.exports = function (options) {

    var SearchEngine = {};

    var defaultOption = {'indexPath': constants.DATA_FOLDER};
    var options = _.isEmpty(options) ? defaultOption : options;

    SearchEngine.indexing = function (pathToEpubs) {

        if (fs.isEmptySync(pathToEpubs)) {
            return Q.reject(new Error('Can`t index empty folder: ' + pathToEpubs));
        }
        console.log("\n\n\n******************************************************\n");
        console.log("Start normalize epub content\n\n".yellow);

        // normalize the directory path
        pathToEpubs = path.normalize(pathToEpubs);

        return preparer.normalize(pathToEpubs, options)
            .then(function (dataSet) {
                console.log("\n******************************************************\n");
                console.log("Ready with normalize epub content\n\n".yellow);

                if (dataSet.length > 0)
                    console.log("Start indexing epub-data.");
                else {
                    console.log("DONE! Nothing to do, epubs already indexed.\n\n");
                    return;
                }

                return SearchEngine.add(dataSet);
            });
    };

    SearchEngine.add = function (jsonDoc) {

        var ids = jsonDoc.FirstSpineItemsId;
        delete jsonDoc.FirstSpineItemsId;

        return SearchEngine._add(jsonDoc, getIndexOptions());
    };

    SearchEngine.search = function (searchFor, bookTitle) {

        bookTitle = bookTitle || DEFAULT_EPUB_TITLE; // * if bookTitle undefined return all hits

        var q = {};
        q.query =
            {
                AND: {
                    'epubTitle': [preparer.normalizeEpupTitle(bookTitle)],
                    'body': [searchFor]
                }
            };

        return SearchEngine.query(q, searchFor);
    };

    SearchEngine.query = function (query, searchFor) {

        return SearchEngine._search(query)
            .then(function (result) {
                const hits = [];

                if (!result.hits) {
                    return hits;
                }

                result.hits.forEach(function (hit) {
                    const document = hit.document,
                        idData = document.id.split(':');

                    document.id = idData[0];

                    const cfiList = cfi.generate({
                        "searchFor": searchFor,
                        "spineItemPath": document.spineItemPath,
                        "baseCfi": document.baseCfi
                    });

                    if (cfiList.length > 0) {
                        document.cfis = cfiList;
                        delete document['*'];
                        delete document.spineItemPath;

                        hits.push(document);
                    }

                });
                return hits;
            });
    };

    SearchEngine.match = function (beginsWith, epubTitle) {

        if (!_.isString(epubTitle) && !_.isNull(epubTitle))
            console.error('epubTitle should be null or type string');

        var epubTitle = epubTitle || DEFAULT_EPUB_TITLE;

        return SearchEngine._match({beginsWith: beginsWith, type: 'ID'})
            .then(function (matches) {
                return filterMatches(matches, epubTitle);
            });
    };

    SearchEngine.empty = function () {
        return SearchEngine._empty();
    };

    SearchEngine.close = function () {
        return SearchEngine._close();
    };

// private
    function getIndexOptions() {
        return {
            fieldOptions: [
                {fieldName: 'epubTitle', searchable: false, store: true},
                {fieldName: 'spineItemPath', searchable: false, store: true},
                {fieldName: 'href', searchable: false, store: true},
                {fieldName: 'baseCfi', searchable: false, store: true},
                {fieldName: 'id', searchable: false, store: true},
                {fieldName: 'filename', searchable: true, store: true},
                {fieldName: 'title', searchable: true, store: false},
                {fieldName: 'body', searchable: true, store: false}
            ]
        };
    }

    function filterMatches(matches, epubTitle) {

        return matches
            .map(function (match) {

                if (epubTitle === '*') {
                    // if epubTitle undefined return all matches
                    return match[0];
                } else {
                    var titles = match[1].map(function (id) {
                        // id = spineitemid:epubtitle
                        return id.split(':')[1]
                    });
                    return _.include(titles, epubTitle) ? match[0] : '';
                }
            })
            .filter(Boolean); // filter ["", "", ""] -> []
    }

    return searchIndex(options)
        .then(function (si) {
            SearchEngine.si = si;
            SearchEngine._search = Q.nbind(si.search, si);
            SearchEngine._close = Q.nbind(si.close, si);
            SearchEngine._empty = Q.nbind(si.empty, si);
            SearchEngine._match = Q.nbind(si.match, si);
            SearchEngine._add = Q.nbind(si.add, si);
            return SearchEngine;
        });
};

/* require('search-index')({
 nGramLength: [1, 2, 3],   // allow phrase search on phrases of length 1, 2 and 3 words
 stopwords: []             // dont strip out stopwords
 }, function(err, si) {
 si.add([
 {date: 1464122926, text: 'some text'},
 {date: 1464122916, text: 'some another text'}
 ], function (err) {
 if (! err) {
 var q = {};
 q.query = { AND: [{'*': ['text']}] };
 si.search(q, function (err, results) {
 console.log(results);
 })
 }
 })
 })
 1. Use nGramLength to activate phrase search

 2. Set stopwords to []. By default, search-index will remove common english words like 'some' and 'another',
 so you need to empty your stopword list to allow them to
 be searchable (you could also remove them from the stopword list individually) */<|endoftext|>


<commit_before>
/**
 * Test dependencies.
 */

var assert = require('assert');
var spread = require('..');

describe("expand arguments", function() {

	var args;
	beforeEach(function() {
		args = function() {
			return arguments;
		};
	});

	it('should transform arguments into an array', function() {
		var arr = spread(args('olivier', 'amy'));
		assert(arr instanceof Array);
		assert.deepEqual(arr, ['olivier', 'amy']);
	});

	it('should concatenate arguments', function() {
		var arr = spread('hello', args('olivier', 'amy'), 'bruno');
		assert(arr instanceof Array);
		assert.deepEqual(arr, ['hello', 'olivier', 'amy', 'bruno']);
	});

});
<commit_msg>should be a better push<commit_after>
/**
 * Test dependencies.
 */

var assert = require('assert');
var spread = require('..');

describe("expand arguments", function() {

	var args;
	beforeEach(function() {
		args = function() {
			return arguments;
		};
	});

	it('should transform arguments into an array', function() {
		var arr = spread(args('olivier', 'amy'));
		assert(arr instanceof Array);
		assert.deepEqual(arr, ['olivier', 'amy']);
	});

	it('should concatenate arguments', function() {
		var arr = spread('hello', args('olivier', 'amy'), 'bruno');
		assert(arr instanceof Array);
		assert.deepEqual(arr, ['hello', 'olivier', 'amy', 'bruno']);
	});

	it('should be a better array push', function() {
		var arr = spread('hello', args('olivier', 'amy'), ['bruno', 'anne']);
		assert(arr instanceof Array);
		assert.deepEqual(arr, ['hello', 'olivier', 'amy', 'bruno', 'anne']);
	});

});
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  extends: ['octane', 'stylistic'],
  rules: {
    'no-bare-strings': 'off',
    'no-action': 'off',
    'no-duplicate-landmark-elements': 'warn',
    'no-implicit-this': {
      allow: ['supported-auth-backends'],
    },
    'require-input-label': 'off',
    'no-down-event-binding': 'warn',
    'self-closing-void-elements': 'off',
  },
  ignore: ['lib/story-md', 'tests/**'],
};
<commit_msg>updates template lint config to override rules in test files for ember language server (#13632)<commit_after>'use strict';
const recommended = require('ember-template-lint/lib/config/recommended').rules; // octane extends recommended - no additions as of 3.14
const stylistic = require('ember-template-lint/lib/config/stylistic').rules;

const testOverrides = { ...recommended, ...stylistic };
for (const key in testOverrides) {
  testOverrides[key] = false;
}

module.exports = {
  extends: ['octane', 'stylistic'],
  rules: {
    'no-bare-strings': 'off',
    'no-action': 'off',
    'no-duplicate-landmark-elements': 'warn',
    'no-implicit-this': {
      allow: ['supported-auth-backends'],
    },
    'require-input-label': 'off',
    'no-down-event-binding': 'warn',
    'self-closing-void-elements': 'off',
  },
  ignore: ['lib/story-md', 'tests/**'],
  // ember language server vscode extension does not currently respect the ignore field
  // override all rules manually as workround to align with cli
  overrides: [
    {
      files: ['**/*-test.js'],
      rules: testOverrides,
    },
  ],
};
<|endoftext|>


<commit_before>uv.StackedBarGraph = function (graphdef, config) {
  var self = this;
  uv.Graph.call(self, graphdef, config).setDefaults().init();

  self.bargroups = {};

  var bargroup, bars, idx, len, color,
    domainData = self.labels,
    csum = domainData.map(function (d) {return 0; }),
    tsum = domainData.map(function (d) {return 0; });

  self.axes[self.config.graph.orientation === 'Horizontal' ? 'ver' : 'hor'].scale.domain(domainData);

  for (idx = 0, len = self.categories.length; idx < len; idx = idx + 1) {
    self.bargroups[self.categories[idx]] = self.chart.append('g').classed('cg-' + uv.util.formatClassName(self.categories[idx]), true);
    self['draw' + self.config.graph.orientation + 'Bars'](idx, csum, tsum);
  }

  self.finalize();
};

uv.StackedBarGraph.prototype = uv.util.inherits(uv.Graph);

uv.StackedBarGraph.prototype.setDefaults = function () {
  var self = this;
  self.graphdef.stepup = 'stepup';
  return this;
};

uv.StackedBarGraph.prototype.drawHorizontalBars = function (idx, csum, tsum) {
  var self = this,
    axes = this.axes,
    color = uv.util.getColorBand(this.config, idx),
    config = this.config,
    bargroup = this.bargroups[this.categories[idx]];

  var bars = bargroup.selectAll('g').data(this.graphdef.dataset[self.categories[idx]])
        .enter().append('g').classed('cge-' + uv.util.formatClassName(self.categories[idx]), true);

  bars.append('rect')
    .attr('height', axes.ver.scale.rangeBand())
    .attr('width', 0)
    .attr('x', function (d, i) { var value = axes.hor.scale(csum[i]); csum[i] += d.value; return value; })
    .attr('y', function (d) {return axes.ver.scale(d.name); })
    .classed('cr-' + uv.util.formatClassName(self.categories[idx]), true)
    .style('stroke', 'none')
    .style('fill', color)
    .transition()
      .duration(self.config.effects.duration)
      .delay(idx * self.config.effects.duration)
      .attr('width', function (d,i) { return axes.hor.scale(csum[i]) - axes.hor.scale(csum[i]-d.value); })
      .each("end", function (d,i){
        d3.select(this).on('mouseover', uv.effects.bar.mouseover(self, idx, self.config.effects.textcolor));
        d3.select(this).on('mouseout', uv.effects.bar.mouseout(self, idx, self.config.effects.textcolor));
        if(typeof self.config.graph.clickCallback === "function") {
          d3.select(this).on('click', function(_d){
              self.config.graph.clickCallback.apply(null, [_d]);
          });
        }
      });

  bars.append('text')
    .attr('y', function(d) { return axes.ver.scale(d.name) + axes.ver.scale.rangeBand()/2; })
    .attr('dx', 0)
    .attr('dy', '.35em')
    .attr('text-anchor', 'end')
    .classed('cr-' + uv.util.formatClassName(self.categories[idx]), true)
    .style('fill', self.config.label.showlabel ? self.config.effects.textcolor : 'none')
    .style('font-family', config.bar.fontfamily)
    .style('font-size', config.bar.fontsize)
    .style('font-weight', config.bar.fontweight)
    .style('opacity', 0)
    .text(function(d) { return ( axes.hor.scale(d.value) > 15 ) ? uv.util.getLabelValue(self, d) : null; })
    .transition()
      .duration(self.config.effects.duration)
      .delay(idx * self.config.effects.duration)
      .style('opacity', 1)
      .attr('x', function (d, i) { tsum[i] += d.value; return axes.hor.scale(tsum[i]) - 5; });

  bars.append('svg:title')
    .text( function (d, i) { return uv.util.getTooltipText(self, self.categories[idx], self.labels[i], d);});
};

uv.StackedBarGraph.prototype.drawVerticalBars = function (idx, csum, tsum) {
  var self = this,
    height = this.height(),
    axes = this.axes,
    color = uv.util.getColorBand(this.config, idx),
    config = this.config,
    bargroup = this.bargroups[self.categories[idx]];

  var bars = bargroup.selectAll('g').data(this.graphdef.dataset[self.categories[idx]])
        .enter().append('g').classed('cge-' + uv.util.formatClassName(self.categories[idx]), true);

  bars.append('rect')
    .attr('height', 0)
    .attr('width', axes.hor.scale.rangeBand())
    .attr('x', function (d) { return axes.hor.scale(d.name); })
    .attr('y', function (d, i) { var value = axes.ver.scale(csum[i]); csum[i] -= d.value; return value; })
    .classed('cr-' + uv.util.formatClassName(self.categories[idx]), true)
    .style('stroke', 'none')
    .style('fill', color)
    .transition()
      .duration(self.config.effects.duration)
      .delay(idx * self.config.effects.duration)
      .attr('height', function (d,i) { return -(axes.ver.scale(-csum[i]) - axes.ver.scale(-csum[i]-d.value)); })
      .each("end", function (d,i){
        d3.select(this).on('mouseover', uv.effects.bar.mouseover(self, idx, self.config.effects.textcolor));
        d3.select(this).on('mouseout', uv.effects.bar.mouseout(self, idx, self.config.effects.textcolor));
        if(typeof self.config.graph.clickCallback === "function") {
          d3.select(this).on('click', function(_d){
            self.config.graph.clickCallback.apply(null, [_d]);
          });
        }
      });

  bars.append('text').attr('transform','scale(1,-1)')
    .attr('x', function(d) { return axes.hor.scale(d.name) + axes.hor.scale.rangeBand()/2; })
    .attr('y', -height + 5)
    .attr('dy', '.71em')
    .attr('text-anchor', 'middle')
    .classed('cr-' + uv.util.formatClassName(self.categories[idx]), true)
    .style('fill', self.config.label.showlabel ? self.config.effects.textcolor : 'none')
    .style('font-family', config.bar.fontfamily)
    .style('font-size', config.bar.fontsize)
    .style('font-weight', config.bar.fontweight)
    .style('opacity', 0)
    .text(function(d) { return ( height - axes.ver.scale(d.value) > 15) ? uv.util.getLabelValue(self, d) : null; })
    .transition()
      .duration(self.config.effects.duration)
      .delay(idx * self.config.effects.duration)
      .style('opacity', 1)
      .attr('y', function (d, i) { tsum[i] += d.value; return -(2*height - axes.ver.scale(tsum[i])) + 5; });

  bars.append('svg:title')
    .text( function (d, i) { return uv.util.getTooltipText(self, self.categories[idx], self.labels[i], d);});

  bargroup.attr('transform', 'translate(0,' + 2 * this.height() + ') scale(1,-1)');
};
<commit_msg>on stacked chart add possibility to override the hover effect with graph.hoverCallback config function<commit_after>uv.StackedBarGraph = function (graphdef, config) {
  var self = this;
  uv.Graph.call(self, graphdef, config).setDefaults().init();

  self.bargroups = {};

  var bargroup, bars, idx, len, color,
    domainData = self.labels,
    csum = domainData.map(function (d) {return 0; }),
    tsum = domainData.map(function (d) {return 0; });

  self.axes[self.config.graph.orientation === 'Horizontal' ? 'ver' : 'hor'].scale.domain(domainData);

  for (idx = 0, len = self.categories.length; idx < len; idx = idx + 1) {
    self.bargroups[self.categories[idx]] = self.chart.append('g').classed('cg-' + uv.util.formatClassName(self.categories[idx]), true);
    self['draw' + self.config.graph.orientation + 'Bars'](idx, csum, tsum);
  }

  self.finalize();
};

uv.StackedBarGraph.prototype = uv.util.inherits(uv.Graph);

uv.StackedBarGraph.prototype.setDefaults = function () {
  var self = this;
  self.graphdef.stepup = 'stepup';
  return this;
};

uv.StackedBarGraph.prototype.drawHorizontalBars = function (idx, csum, tsum) {
  var self = this,
    axes = this.axes,
    color = uv.util.getColorBand(this.config, idx),
    config = this.config,
    bargroup = this.bargroups[this.categories[idx]];

  var bars = bargroup.selectAll('g').data(this.graphdef.dataset[self.categories[idx]])
        .enter().append('g').classed('cge-' + uv.util.formatClassName(self.categories[idx]), true);

  bars.append('rect')
    .attr('height', axes.ver.scale.rangeBand())
    .attr('width', 0)
    .attr('x', function (d, i) { var value = axes.hor.scale(csum[i]); csum[i] += d.value; return value; })
    .attr('y', function (d) {return axes.ver.scale(d.name); })
    .classed('cr-' + uv.util.formatClassName(self.categories[idx]), true)
    .style('stroke', 'none')
    .style('fill', color)
    .transition()
      .duration(self.config.effects.duration)
      .delay(idx * self.config.effects.duration)
      .attr('width', function (d,i) { return axes.hor.scale(csum[i]) - axes.hor.scale(csum[i]-d.value); })
      .each("end", function (d,i){
        if(typeof self.config.graph.hoverCallback === "function") {
          d3.select(this).on('mouseover', function(_d){
              self.config.graph.hoverCallback.apply(null, [true,_d]);
          });
          d3.select(this).on('mouseout', function(_d){
              self.config.graph.hoverCallback.apply(null, [false,_d]);
          });
        } else {
          d3.select(this).on('mouseover', uv.effects.bar.mouseover(self, idx, self.config.effects.textcolor));
          d3.select(this).on('mouseout', uv.effects.bar.mouseout(self, idx, self.config.effects.textcolor));
        }
        if(typeof self.config.graph.clickCallback === "function") {
          d3.select(this).on('click', function(_d){
              self.config.graph.clickCallback.apply(null, [_d]);
          });
        }
      });

  bars.append('text')
    .attr('y', function(d) { return axes.ver.scale(d.name) + axes.ver.scale.rangeBand()/2; })
    .attr('dx', 0)
    .attr('dy', '.35em')
    .attr('text-anchor', 'end')
    .classed('cr-' + uv.util.formatClassName(self.categories[idx]), true)
    .style('fill', self.config.label.showlabel ? self.config.effects.textcolor : 'none')
    .style('font-family', config.bar.fontfamily)
    .style('font-size', config.bar.fontsize)
    .style('font-weight', config.bar.fontweight)
    .style('opacity', 0)
    .text(function(d) { return ( axes.hor.scale(d.value) > 15 ) ? uv.util.getLabelValue(self, d) : null; })
    .transition()
      .duration(self.config.effects.duration)
      .delay(idx * self.config.effects.duration)
      .style('opacity', 1)
      .attr('x', function (d, i) { tsum[i] += d.value; return axes.hor.scale(tsum[i]) - 5; });

  bars.append('svg:title')
    .text( function (d, i) { return uv.util.getTooltipText(self, self.categories[idx], self.labels[i], d);});
};

uv.StackedBarGraph.prototype.drawVerticalBars = function (idx, csum, tsum) {
  var self = this,
    height = this.height(),
    axes = this.axes,
    color = uv.util.getColorBand(this.config, idx),
    config = this.config,
    bargroup = this.bargroups[self.categories[idx]];

  var bars = bargroup.selectAll('g').data(this.graphdef.dataset[self.categories[idx]])
        .enter().append('g').classed('cge-' + uv.util.formatClassName(self.categories[idx]), true);

  bars.append('rect')
    .attr('height', 0)
    .attr('width', axes.hor.scale.rangeBand())
    .attr('x', function (d) { return axes.hor.scale(d.name); })
    .attr('y', function (d, i) { var value = axes.ver.scale(csum[i]); csum[i] -= d.value; return value; })
    .classed('cr-' + uv.util.formatClassName(self.categories[idx]), true)
    .style('stroke', 'none')
    .style('fill', color)
    .transition()
      .duration(self.config.effects.duration)
      .delay(idx * self.config.effects.duration)
      .attr('height', function (d,i) { return -(axes.ver.scale(-csum[i]) - axes.ver.scale(-csum[i]-d.value)); })
      .each("end", function (d,i){
        if(typeof self.config.graph.hoverCallback === "function") {
          d3.select(this).on('mouseover', function(_d){
              self.config.graph.hoverCallback.apply(null, [true,_d]);
          });
          d3.select(this).on('mouseout', function(_d){
              self.config.graph.hoverCallback.apply(null, [false,_d]);
          });
        } else {
          d3.select(this).on('mouseover', uv.effects.bar.mouseover(self, idx, self.config.effects.textcolor));
          d3.select(this).on('mouseout', uv.effects.bar.mouseout(self, idx, self.config.effects.textcolor));
        }
        if(typeof self.config.graph.clickCallback === "function") {
          d3.select(this).on('click', function(_d){
            self.config.graph.clickCallback.apply(null, [_d]);
          });
        }
      });

  bars.append('text').attr('transform','scale(1,-1)')
    .attr('x', function(d) { return axes.hor.scale(d.name) + axes.hor.scale.rangeBand()/2; })
    .attr('y', -height + 5)
    .attr('dy', '.71em')
    .attr('text-anchor', 'middle')
    .classed('cr-' + uv.util.formatClassName(self.categories[idx]), true)
    .style('fill', self.config.label.showlabel ? self.config.effects.textcolor : 'none')
    .style('font-family', config.bar.fontfamily)
    .style('font-size', config.bar.fontsize)
    .style('font-weight', config.bar.fontweight)
    .style('opacity', 0)
    .text(function(d) { return ( height - axes.ver.scale(d.value) > 15) ? uv.util.getLabelValue(self, d) : null; })
    .transition()
      .duration(self.config.effects.duration)
      .delay(idx * self.config.effects.duration)
      .style('opacity', 1)
      .attr('y', function (d, i) { tsum[i] += d.value; return -(2*height - axes.ver.scale(tsum[i])) + 5; });

  bars.append('svg:title')
    .text( function (d, i) { return uv.util.getTooltipText(self, self.categories[idx], self.labels[i], d);});

  bargroup.attr('transform', 'translate(0,' + 2 * this.height() + ') scale(1,-1)');
};
<|endoftext|>


<commit_before>define([
	'goo/sound/AudioContext',
	'goo/math/MathUtils',
	'goo/util/rsvp'
], function (
	AudioContext,
	MathUtils,
	RSVP
) {
	'use strict';

	/**
	 * A representation of a sound in the engine
	 */
	function Sound() {
		/** @type {string}
		 */
		this.id = null;
		/** @type {string}
		 */
		this.name = null;
		this._loop = false;
		this._rate = 1.0;
		this._offset = 0;
		this._duration = null;
		this._volume = 1.0;

		// Nodes
		this._buffer = null;
		this._stream = null;
		this._streamSource = null;
		this._currentSource = null;
		this._outNode = AudioContext.getContext().createGain();
		this.connectTo();

		// Playback memory
		this._playStart = 0;
		this._pausePos = 0;
		//this._endTimer = null;
		this._endPromise = null;

		this._paused = false;

		// #ifdef DEBUG
		Object.seal(this);
		// #endif
	}

	/**
	 * Plays the sound if it's not playing
	 * @returns {RSVP.Promise} Resolves when sound has played through or when it's stopped.
	 * Looping sounds will never resolve
	 */
	Sound.prototype.play = function () {
		if (this._currentSource) {
			return this._endPromise;
		}
		this._endPromise = new RSVP.Promise(); //! AT: this needs refactoring
		if (!this._buffer || this._stream) {
			return this._endPromise;
		}

		var currentSource = this._currentSource = AudioContext.getContext().createBufferSource();

		this._paused = false;
		this._currentSource.onended = function () {
			if (this._currentSource === currentSource && !this._paused) {
				this.stop();
			}
		}.bind(this);

		this._currentSource.playbackRate.value = this._rate;
		this._currentSource.connect(this._outNode);
		this._currentSource.buffer = this._buffer;
		this._currentSource.loop = this._loop;
		if (this._loop) {
			this._currentSource.loopStart = this._offset;
			this._currentSource.loopEnd = this._duration + this._offset;
		}

		this._playStart = AudioContext.getContext().currentTime - this._pausePos;
		var duration = this._duration - this._pausePos;

		if (this._loop) {
			this._currentSource.start();
		} else {
			this._currentSource.start(0, this._pausePos + this._offset, duration);
		}

		return this._endPromise;
	};

	/**
	 * Pauses the sound if it's playing
	 */
	Sound.prototype.pause = function () {

		if (!this._currentSource) {
			return;
		}
		this._paused = true;

		this._pausePos = (AudioContext.getContext().currentTime - this._playStart) % this._duration;
		this._pausePos /= this._rate;
		this._stop();
	};

	/**
	 * Stops the sound if it's playing
	 */
	Sound.prototype.stop = function () {
		this._paused = false;
		this._pausePos = 0;
		if (this._endPromise) {
			this._endPromise.resolve();
		}
		if (this._currentSource) {
			this._stop();
		}
	};

	Sound.prototype.fadeIn = function (time) {
		this.stop();
		var volume = this._volume;
		this._outNode.gain.value = 0;
		var p = this.fade(volume, time);
		this.play();
		return p;
	};

	Sound.prototype.fadeOut = function (time) {
		return this.fade(0, time);
	};

	Sound.prototype.fade = function (volume, time) {
		this._outNode.gain.setValueAtTime(this._outNode.gain.value, AudioContext.getContext().currentTime);
		this._outNode.gain.linearRampToValueAtTime(volume, AudioContext.getContext().currentTime + time);
		var p = new RSVP.Promise();
		setTimeout(function () {
			p.resolve();
		}, time * 1000);
		return p;
	};

	Sound.prototype.isPlaying = function () {
		return !!this._currentSource;
	};

	/**
	 * Does the actual stopping of the sound
	 * @private
	 */
	Sound.prototype._stop = function () {
		this._currentSource.stop(0);
		this._currentSource = null;
	};

	/**
	 * Updates the sound according to config
	 * @param {object} [config]
	 * @param {boolean} [config.loop]
	 * @param {number} [config.volume]
	 * @param {number} [config.name] The sound name
	 * @param {number} [config.start] Start offset in seconds.
	 * Will be clamped to be in actual soundclip duration
	 * @param {number} [config.duration] Duration of the sound.
	 * Will be clamped to be in actual soundclip duration
	 * @param {number} [config.timeScale] Playback rate of the sound
	 */
	Sound.prototype.update = function (config) {
		config = config || {};
		if (config.id !== undefined) {
			this.id = config.id;
		}
		if (config.name !== undefined) {
			this.name = config.name;
		}
		if (config.loop !== undefined) {
			this._loop = !!config.loop;
			if (this._currentSource) {
				this._currentSource.loop = this._loop;
			}
		}
		if (config.volume !== undefined) {
			this._volume = MathUtils.clamp(config.volume, 0, 1);
			this._outNode.gain.value = this._volume;
		}
		if (config.offset !== undefined) {
			this._offset = config.offset;
		}
		if (config.duration !== undefined) {
			this._duration = config.duration;
		}
		if (config.timeScale !== undefined) {
			this._rate = config.timeScale;
			//! AT: should have the same name if they are the same thing
			// problem is that there are plenty of projects out there that have timeScale instead of rate
			// timeScale was considered because it's the same as for animations
			// rate would have been preferred to timeScale as it's the term used by WebAudio
			if (this._currentSource) {
				this._currentSource.playbackRate.value = config.timeScale;
			}
		}
		if (this._buffer) {
			this._clampInterval();
		}
	};

	/**
	 * Clamps the start offset and duration to be in sound range
	 * @private
	 */
	Sound.prototype._clampInterval = function () {
		this._offset = Math.min(this._offset, this._buffer.duration);
		if (this._duration !== null) {
			this._duration = Math.min(this._buffer.duration - this._offset, this._duration);
		} else {
			this._duration = this._buffer.duration - this._offset;
		}
		this._pausePos = MathUtils.clamp(this._pausePos, 0, this._duration);
	};

	/**
	 * Connect output of sound to audionodes
	 * @param {AudioNode[]|AudioNode} nodes
	 */
	Sound.prototype.connectTo = function (nodes) {
		this._outNode.disconnect();
		if (!nodes) {
			return;
		}
		if (!(nodes instanceof Array)) {
			nodes = [nodes];
		}
		for (var i = 0; i < nodes.length; i++) {
			this._outNode.connect(nodes[i]);
		}
	};

	/**
	 * Sets the audio buffer which will be the sound source
	 * @param {AudioBuffer} buffer
	 */
	Sound.prototype.setAudioBuffer = function (buffer) {
		this.setAudioStream(null);
		this._buffer = buffer;
		this._clampInterval();
	};

	Sound.prototype.setAudioStream = function (stream) {
		if (!stream) {
			if (this._streamSource) {
				this._streamSource.disconnect();
				this._streamSource = null;
			}
			return;
		}
		this.stop();
		this._stream = stream;
		this._streamSource = AudioContext.getContext().createMediaStreamSource(stream);
		this._streamSource.connect(this._outNode);
	};

	return Sound;
});<commit_msg>Fixing the soundplay bug and adding a when parameter while at it<commit_after>define([
	'goo/sound/AudioContext',
	'goo/math/MathUtils',
	'goo/util/rsvp'
], function (
	AudioContext,
	MathUtils,
	RSVP
) {
	'use strict';

	/**
	 * A representation of a sound in the engine
	 */
	function Sound() {
		/** @type {string}
		 */
		this.id = null;
		/** @type {string}
		 */
		this.name = null;
		this._loop = false;
		this._rate = 1.0;
		this._offset = 0;
		this._duration = null;
		this._volume = 1.0;

		// Nodes
		this._buffer = null;
		this._stream = null;
		this._streamSource = null;
		this._currentSource = null;
		this._outNode = AudioContext.getContext().createGain();
		this.connectTo();

		// Playback memory
		this._playStart = 0;
		this._pausePos = 0;
		//this._endTimer = null;
		this._endPromise = null;

		this._paused = false;

		// #ifdef DEBUG
		Object.seal(this);
		// #endif
	}

	/**
	 * Plays the sound if it's not playing
	 * @param {number} when Time in seconds according to [AudioContext.currentTime]{@link https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/currentTime} when sound should start to play.
	 * @returns {RSVP.Promise} Resolves when sound has played through or when it's stopped.
	 * Looping sounds will never resolve
	 */
	Sound.prototype.play = function (when) {
		when = when || 0;
		if (this._currentSource) {
			return this._endPromise;
		}
		this._endPromise = new RSVP.Promise(); //! AT: this needs refactoring
		if (!this._buffer || this._stream) {
			return this._endPromise;
		}

		var currentSource = this._currentSource = AudioContext.getContext().createBufferSource();

		this._paused = false;
		this._currentSource.onended = function () {
			if (this._currentSource === currentSource && !this._paused) {
				this.stop();
			}
		}.bind(this);

		this._currentSource.playbackRate.value = this._rate;
		this._currentSource.connect(this._outNode);
		this._currentSource.buffer = this._buffer;
		this._currentSource.loop = this._loop;
		if (this._loop) {
			this._currentSource.loopStart = this._offset;
			this._currentSource.loopEnd = this._duration + this._offset;
		}

		this._playStart = AudioContext.getContext().currentTime - this._pausePos;
		var duration = this._duration - this._pausePos;

		if (this._loop) {
			this._currentSource.start(when, this._pausePos + this._offset);
		} else {
			this._currentSource.start(when, this._pausePos + this._offset, duration);
		}

		return this._endPromise;
	};

	/**
	 * Pauses the sound if it's playing
	 */
	Sound.prototype.pause = function () {

		if (!this._currentSource) {
			return;
		}
		this._paused = true;

		this._pausePos = (AudioContext.getContext().currentTime - this._playStart) % this._duration;
		this._pausePos /= this._rate;
		this._stop();
	};

	/**
	 * Stops the sound if it's playing
	 */
	Sound.prototype.stop = function () {
		this._paused = false;
		this._pausePos = 0;
		if (this._endPromise) {
			this._endPromise.resolve();
		}
		if (this._currentSource) {
			this._stop();
		}
	};

	Sound.prototype.fadeIn = function (time) {
		this.stop();
		var volume = this._volume;
		this._outNode.gain.value = 0;
		var p = this.fade(volume, time);
		this.play();
		return p;
	};

	Sound.prototype.fadeOut = function (time) {
		return this.fade(0, time);
	};

	Sound.prototype.fade = function (volume, time) {
		this._outNode.gain.setValueAtTime(this._outNode.gain.value, AudioContext.getContext().currentTime);
		this._outNode.gain.linearRampToValueAtTime(volume, AudioContext.getContext().currentTime + time);
		var p = new RSVP.Promise();
		setTimeout(function () {
			p.resolve();
		}, time * 1000);
		return p;
	};

	Sound.prototype.isPlaying = function () {
		return !!this._currentSource;
	};

	/**
	 * Does the actual stopping of the sound
	 * @private
	 */
	Sound.prototype._stop = function () {
		this._currentSource.stop(0);
		this._currentSource = null;
	};

	/**
	 * Updates the sound according to config
	 * @param {object} [config]
	 * @param {boolean} [config.loop]
	 * @param {number} [config.volume]
	 * @param {number} [config.name] The sound name
	 * @param {number} [config.start] Start offset in seconds.
	 * Will be clamped to be in actual soundclip duration
	 * @param {number} [config.duration] Duration of the sound.
	 * Will be clamped to be in actual soundclip duration
	 * @param {number} [config.timeScale] Playback rate of the sound
	 */
	Sound.prototype.update = function (config) {
		config = config || {};
		if (config.id !== undefined) {
			this.id = config.id;
		}
		if (config.name !== undefined) {
			this.name = config.name;
		}
		if (config.loop !== undefined) {
			this._loop = !!config.loop;
			if (this._currentSource) {
				this._currentSource.loop = this._loop;
			}
		}
		if (config.volume !== undefined) {
			this._volume = MathUtils.clamp(config.volume, 0, 1);
			this._outNode.gain.value = this._volume;
		}
		if (config.offset !== undefined) {
			this._offset = config.offset;
		}
		if (config.duration !== undefined) {
			this._duration = config.duration;
		}
		if (config.timeScale !== undefined) {
			this._rate = config.timeScale;
			//! AT: should have the same name if they are the same thing
			// problem is that there are plenty of projects out there that have timeScale instead of rate
			// timeScale was considered because it's the same as for animations
			// rate would have been preferred to timeScale as it's the term used by WebAudio
			if (this._currentSource) {
				this._currentSource.playbackRate.value = config.timeScale;
			}
		}
		if (this._buffer) {
			this._clampInterval();
		}
	};

	/**
	 * Clamps the start offset and duration to be in sound range
	 * @private
	 */
	Sound.prototype._clampInterval = function () {
		this._offset = Math.min(this._offset, this._buffer.duration);
		if (this._duration !== null) {
			this._duration = Math.min(this._buffer.duration - this._offset, this._duration);
		} else {
			this._duration = this._buffer.duration - this._offset;
		}
		this._pausePos = MathUtils.clamp(this._pausePos, 0, this._duration);
	};

	/**
	 * Connect output of sound to audionodes
	 * @param {AudioNode[]|AudioNode} nodes
	 */
	Sound.prototype.connectTo = function (nodes) {
		this._outNode.disconnect();
		if (!nodes) {
			return;
		}
		if (!(nodes instanceof Array)) {
			nodes = [nodes];
		}
		for (var i = 0; i < nodes.length; i++) {
			this._outNode.connect(nodes[i]);
		}
	};

	/**
	 * Sets the audio buffer which will be the sound source
	 * @param {AudioBuffer} buffer
	 */
	Sound.prototype.setAudioBuffer = function (buffer) {
		this.setAudioStream(null);
		this._buffer = buffer;
		this._clampInterval();
	};

	Sound.prototype.setAudioStream = function (stream) {
		if (!stream) {
			if (this._streamSource) {
				this._streamSource.disconnect();
				this._streamSource = null;
			}
			return;
		}
		this.stop();
		this._stream = stream;
		this._streamSource = AudioContext.getContext().createMediaStreamSource(stream);
		this._streamSource.connect(this._outNode);
	};

	return Sound;
});<|endoftext|>


<commit_before>var Camera = {
    x: 0,
    y: 0,

    applyX: 0,
    applyY: 0,

    yLocked: false,
    xLocked: false,

    isRumbling: false,
    rumbleOffset: 0,
    rumbleIntensity: 1,
    rumbleDuration: 0,

    translateX: function(x) {
        return Math.round(x + this.applyX + this.rumbleOffset);
    },

    translateY: function(y) {
        return Math.round(y + this.applyY + this.rumbleOffset);
    },

    translate: function(x, y) {
        return {
            x: this.translateX(x),
            y: this.translateY(y)
        };
    },

    setPos: function(x, y) {
        this.x = x;
        this.y = y;
    },

    trackingEntity: null,

    getScreenWidth: function () {
        return Canvas.canvas.width;
    },

    getScreenHeight: function () {
        return Canvas.canvas.height;
    },

    trackHard: false,

    followEntity: function(e, hard) {
        this.trackingEntity = e;
        this.trackHard = !!hard;
    },

    rumble: function(duration, intensity) {
        this.isRumbling = true;
        this.rumbleOffset = 0;
        this.rumbleDuration = duration;
        this.rumbleIntensity = intensity;
    },

    update: function() {
        if (this.isRumbling) {
            this.rumbleDuration--;

            this.rumbleOffset = chance.integer({
                min: -this.rumbleIntensity,
                max: this.rumbleIntensity
            });

            if (this.rumbleDuration <= 0) {
                this.isRumbling = false;
                this.rumbleOffset = 0;
            }
        }

        if (this.trackingEntity != null) {
            this.x = this.getScreenWidth() / 2 - this.trackingEntity.posX - this.trackingEntity.width / 2;
            this.y = this.getScreenHeight() / 2 - this.trackingEntity.posY - this.trackingEntity.height / 2;
        }

        if (this.trackHard) {
            this.applyX = this.x;
            this.applyY = this.y;
            this.trackHard = false;
        } else {
            this.applyX = MathHelper.lerp(this.applyX, this.x, 0.1);
            this.applyY = MathHelper.lerp(this.applyY, this.y, 0.1);
        }
    }
};<commit_msg>Camera: Do not reset rumble effect when starting a new rumble (avoids any short lived effects from being a bit weird or hard to notice)<commit_after>var Camera = {
    x: 0,
    y: 0,

    applyX: 0,
    applyY: 0,

    yLocked: false,
    xLocked: false,

    isRumbling: false,
    rumbleOffset: 0,
    rumbleIntensity: 1,
    rumbleDuration: 0,

    translateX: function(x) {
        return Math.round(x + this.applyX + this.rumbleOffset);
    },

    translateY: function(y) {
        return Math.round(y + this.applyY + this.rumbleOffset);
    },

    translate: function(x, y) {
        return {
            x: this.translateX(x),
            y: this.translateY(y)
        };
    },

    setPos: function(x, y) {
        this.x = x;
        this.y = y;
    },

    trackingEntity: null,

    getScreenWidth: function () {
        return Canvas.canvas.width;
    },

    getScreenHeight: function () {
        return Canvas.canvas.height;
    },

    trackHard: false,

    followEntity: function(e, hard) {
        this.trackingEntity = e;
        this.trackHard = !!hard;
    },

    rumble: function(duration, intensity) {
        this.isRumbling = true;
        this.rumbleDuration = duration;
        this.rumbleIntensity = intensity;
    },

    update: function() {
        if (this.isRumbling) {
            this.rumbleDuration--;

            this.rumbleOffset = chance.integer({
                min: -this.rumbleIntensity,
                max: this.rumbleIntensity
            });

            if (this.rumbleDuration <= 0) {
                this.isRumbling = false;
                this.rumbleOffset = 0;
            }
        }

        if (this.trackingEntity != null) {
            this.x = this.getScreenWidth() / 2 - this.trackingEntity.posX - this.trackingEntity.width / 2;
            this.y = this.getScreenHeight() / 2 - this.trackingEntity.posY - this.trackingEntity.height / 2;
        }

        if (this.trackHard) {
            this.applyX = this.x;
            this.applyY = this.y;
            this.trackHard = false;
        } else {
            this.applyX = MathHelper.lerp(this.applyX, this.x, 0.1);
            this.applyY = MathHelper.lerp(this.applyY, this.y, 0.1);
        }
    }
};<|endoftext|>


<commit_before>import h from 'hyperscript';
import c from '../utils/prefixClass';

import Controls from '../Controls';
import Page from '../Page';

import errorView from './error';
import orderPagesBooklet from './orderPagesBooklet';
import padPages from './padPages';
import { gridLayout, printLayout, flipLayout } from '../Layouts';

const MODE_FLIP = 'interactive';
const MODE_PREVIEW = 'grid';
const MODE_SHEET = 'print';
const MODE_OUTLINE = 'outline';

const ARRANGE_ONE = 'arrange_one';
const ARRANGE_SPREAD = 'arrange_two';
const ARRANGE_BOOKLET = 'arrange_booklet';

class Viewer {
  constructor({ bindery }) {
    this.book = null;
    this.pageSetup = bindery.pageSetup;

    this.zoomBox = h(c('.zoom-wrap'));
    this.element = h(c('.root'), this.zoomBox);

    this.doubleSided = true;
    this.printArrange = ARRANGE_ONE;
    this.isShowingCropMarks = true;
    this.isShowingBleedMarks = false;

    this.mode = MODE_PREVIEW;
    this.currentLeaf = 0;

    this.listenForPrint();
    this.listenForResize();

    this.setGrid = this.setGrid.bind(this);
    this.setOutline = this.setOutline.bind(this);
    this.setPrint = this.setPrint.bind(this);
    this.setFlip = this.setFlip.bind(this);

    this.controls = new Controls({ binder: bindery, viewer: this });

    this.element.classList.add(c('in-progress'));

    this.element.appendChild(this.controls.element);
    document.body.appendChild(this.element);
  }

  // Automatically switch into print mode
  listenForPrint() {
    if (window.matchMedia) {
      const mediaQueryList = window.matchMedia('print');
      mediaQueryList.addListener((mql) => {
        if (mql.matches) {
          this.setPrint();
        } else {
          // after print
        }
      });
    }
  }

  listenForResize() {
    window.addEventListener('resize', () => {
      if (!this.throttleResize) {
        this.updateZoom();
        this.throttleResize = setTimeout(() => {
          this.throttleResize = null;
        }, 20);
      }
    });
  }

  get isTwoUp() {
    return this.printArrange !== ARRANGE_ONE;
  }

  get isShowingCropMarks() {
    return this.element.classList.contains(c('show-crop'));
  }

  set isShowingCropMarks(newVal) {
    if (newVal) {
      this.element.classList.add(c('show-crop'));
      this.setPrint();
    } else {
      this.element.classList.remove(c('show-crop'));
    }
  }
  get isShowingBleedMarks() {
    return this.element.classList.contains(c('show-bleed-marks'));
  }

  set isShowingBleedMarks(newVal) {
    if (newVal) {
      this.element.classList.add(c('show-bleed-marks'));
      this.setPrint();
    } else {
      this.element.classList.remove(c('show-bleed-marks'));
    }
  }

  setSheetSize(newVal) {
    this.pageSetup.setSheetSizeMode(newVal);
    this.pageSetup.updateStylesheet();

    if (this.mode !== MODE_SHEET) {
      this.setPrint();
    }
    this.updateZoom();
  }

  setPrintArrange(newVal) {
    if (newVal === this.printArrange) return;
    this.printArrange = newVal;

    this.pageSetup.setPrintTwoUp(this.isTwoUp);
    this.pageSetup.updateStylesheet();

    if (this.mode === MODE_SHEET) {
      this.render();
    } else {
      this.setPrint();
    }
  }

  displayError(title, text) {
    if (!this.element.parentNode) {
      document.body.appendChild(this.element);
    }
    if (!this.error) {
      this.zoomBox.innerHTML = '';
      this.error = errorView(title, text);
      this.zoomBox.appendChild(this.error);
    }
  }
  clear() {
    this.book = null;
    this.lastSpreadInProgress = null; // TODO: Make this clearer, after first render
    this.zoomBox.innerHTML = '';
  }
  cancel() {
    // TODO this doesn't work if the target is an existing node
    if (this.element.parentNode) {
      this.element.parentNode.removeChild(this.element);
    }
  }
  toggleGuides() {
    this.element.classList.toggle(c('show-guides'));
  }
  toggleBleed() {
    this.element.classList.add(c('show-bleed'));
  }
  toggleDouble() {
    this.doubleSided = !this.doubleSided;
    this.render();
  }
  setMode(newMode) {
    switch (newMode) {
    case 'grid':
    case 'default':
      this.mode = MODE_PREVIEW;
      break;
    case 'interactive':
    case 'flip':
      this.mode = MODE_FLIP;
      break;
    case 'print':
    case 'sheet':
      this.mode = MODE_SHEET;
      break;
    case 'outline':
    case 'outlines':
      this.mode = MODE_OUTLINE;
      break;
    default:
      console.error(`Bindery: Unknown view mode "${newMode}"`);
      break;
    }
  }
  setGrid() {
    if (this.mode === MODE_PREVIEW) return;
    if (this.mode === MODE_OUTLINE) {
      this.mode = MODE_PREVIEW;
      this.updateGuides();
    } else {
      this.mode = MODE_PREVIEW;
      this.render();
    }
  }
  setOutline() {
    if (this.mode === MODE_OUTLINE) return;
    if (this.mode === MODE_PREVIEW) {
      this.mode = MODE_OUTLINE;
      this.updateGuides();
    } else {
      this.mode = MODE_OUTLINE;
      this.render();
    }
  }
  setPrint() {
    if (this.mode === MODE_SHEET) return;
    this.mode = MODE_SHEET;
    this.render();
  }
  setFlip() {
    this.mode = MODE_FLIP;
    this.render();
  }
  render() {
    if (!this.book) return;
    const { body } = document;

    if (!this.element.parentNode) {
      body.appendChild(this.element);
    }

    this.flaps = [];
    body.classList.add(c('viewing'));
    this.element.setAttribute('bindery-view-mode', this.mode);

    const scrollMax = body.scrollHeight - body.offsetHeight;
    const scrollPct = body.scrollTop / scrollMax;

    if (this.mode === MODE_PREVIEW) {
      this.renderGrid();
    } else if (this.mode === MODE_OUTLINE) {
      this.renderGrid();
    } else if (this.mode === MODE_FLIP) {
      this.renderInteractive();
    } else if (this.mode === MODE_SHEET) {
      this.renderPrint();
    } else {
      this.renderGrid();
    }

    body.scrollTop = scrollMax * scrollPct;
    this.updateZoom();
  }

  renderProgress() {
    const twoPageSpread = function (...arg) {
      return h(c('.spread-wrapper') + c('.spread-size'), ...arg);
    };

    this.book.pages.forEach((page, i) => {
      // If hasn't been added, or not in spread yet
      if (!this.zoomBox.contains(page.element) || page.element.parentNode === this.zoomBox) {
        if (this.lastSpreadInProgress && this.lastSpreadInProgress.children.length < 2) {
          this.lastSpreadInProgress.appendChild(page.element);
        } else {
          if (i === 0) {
            const spacer = new Page();
            spacer.element.style.visibility = 'hidden';
            this.lastSpreadInProgress = twoPageSpread(spacer.element, page.element);
          } else {
            this.lastSpreadInProgress = twoPageSpread(page.element);
          }
          this.zoomBox.appendChild(this.lastSpreadInProgress);
        }
      }
    });

    if (this.book.pageInProgress) {
      this.zoomBox.appendChild(this.book.pageInProgress.element);
    }

    this.updateZoom();
  }

  updateZoom() {
    if (this.zoomBox.firstElementChild) {
      const scrollPct = document.body.scrollTop / document.body.scrollHeight;
      const exportW = this.zoomBox.getBoundingClientRect().width;
      const contentW = this.zoomBox.firstElementChild.getBoundingClientRect().width;

      const scale = Math.min(1, exportW / (contentW + 20));

      this.zoomBox.style.transform = `scale(${scale})`;
      document.body.scrollTop = document.body.scrollHeight * scrollPct;
    }
  }

  updateGuides() {
    this.element.setAttribute('bindery-view-mode', this.mode);
    if (this.mode === MODE_OUTLINE) {
      this.element.classList.add(c('show-bleed'));
      this.element.classList.add(c('show-guides'));
    } else {
      this.element.classList.remove(c('show-bleed'));
      this.element.classList.remove(c('show-guides'));
    }
  }

  renderPrint() {
    this.element.classList.add(c('show-bleed'));
    this.element.classList.remove(c('show-guides'));

    this.zoomBox.innerHTML = '';

    const isBooklet = this.printArrange === ARRANGE_BOOKLET;
    const orient = this.orientation;

    let pages = this.book.pages.slice();
    if (this.printArrange === ARRANGE_SPREAD) {
      pages = padPages(pages);
    } else if (isBooklet) {
      pages = orderPagesBooklet(pages);
    }

    const fragment = printLayout(pages, this.isTwoUp, orient, isBooklet);
    this.zoomBox.appendChild(fragment);
  }

  renderGrid() {
    this.updateGuides();
    this.zoomBox.innerHTML = '';

    let pages = this.book.pages.slice();

    if (this.doubleSided) pages = padPages(pages);

    const fragment = gridLayout(pages, this.doubleSided);
    this.zoomBox.appendChild(fragment);
  }

  renderInteractive() {
    this.zoomBox.innerHTML = '';
    this.flaps = [];

    this.element.classList.remove(c('show-bleed'));
    this.element.classList.remove(c('show-guides'));

    const pages = padPages(this.book.pages.slice());

    const fragment = flipLayout(pages, this.doubleSided);
    this.zoomBox.appendChild(fragment);
  }

}

export default Viewer;
<commit_msg>Preselect tab<commit_after>import h from 'hyperscript';
import c from '../utils/prefixClass';

import Controls from '../Controls';
import Page from '../Page';

import errorView from './error';
import orderPagesBooklet from './orderPagesBooklet';
import padPages from './padPages';
import { gridLayout, printLayout, flipLayout } from '../Layouts';

const MODE_FLIP = 'interactive';
const MODE_PREVIEW = 'grid';
const MODE_SHEET = 'print';
const MODE_OUTLINE = 'outline';

const ARRANGE_ONE = 'arrange_one';
const ARRANGE_SPREAD = 'arrange_two';
const ARRANGE_BOOKLET = 'arrange_booklet';

class Viewer {
  constructor({ bindery }) {
    this.book = null;
    this.pageSetup = bindery.pageSetup;

    this.zoomBox = h(c('.zoom-wrap'));
    this.element = h(c('.root'), this.zoomBox);

    this.doubleSided = true;
    this.printArrange = ARRANGE_ONE;
    this.isShowingCropMarks = true;
    this.isShowingBleedMarks = false;

    this.mode = MODE_PREVIEW;
    this.element.setAttribute('bindery-view-mode', this.mode);
    this.currentLeaf = 0;

    this.listenForPrint();
    this.listenForResize();

    this.setGrid = this.setGrid.bind(this);
    this.setOutline = this.setOutline.bind(this);
    this.setPrint = this.setPrint.bind(this);
    this.setFlip = this.setFlip.bind(this);

    this.controls = new Controls({ binder: bindery, viewer: this });

    this.element.classList.add(c('in-progress'));

    this.element.appendChild(this.controls.element);
    document.body.appendChild(this.element);
  }

  // Automatically switch into print mode
  listenForPrint() {
    if (window.matchMedia) {
      const mediaQueryList = window.matchMedia('print');
      mediaQueryList.addListener((mql) => {
        if (mql.matches) {
          this.setPrint();
        } else {
          // after print
        }
      });
    }
  }

  listenForResize() {
    window.addEventListener('resize', () => {
      if (!this.throttleResize) {
        this.updateZoom();
        this.throttleResize = setTimeout(() => {
          this.throttleResize = null;
        }, 20);
      }
    });
  }

  get isTwoUp() {
    return this.printArrange !== ARRANGE_ONE;
  }

  get isShowingCropMarks() {
    return this.element.classList.contains(c('show-crop'));
  }

  set isShowingCropMarks(newVal) {
    if (newVal) {
      this.element.classList.add(c('show-crop'));
      this.setPrint();
    } else {
      this.element.classList.remove(c('show-crop'));
    }
  }
  get isShowingBleedMarks() {
    return this.element.classList.contains(c('show-bleed-marks'));
  }

  set isShowingBleedMarks(newVal) {
    if (newVal) {
      this.element.classList.add(c('show-bleed-marks'));
      this.setPrint();
    } else {
      this.element.classList.remove(c('show-bleed-marks'));
    }
  }

  setSheetSize(newVal) {
    this.pageSetup.setSheetSizeMode(newVal);
    this.pageSetup.updateStylesheet();

    if (this.mode !== MODE_SHEET) {
      this.setPrint();
    }
    this.updateZoom();
  }

  setPrintArrange(newVal) {
    if (newVal === this.printArrange) return;
    this.printArrange = newVal;

    this.pageSetup.setPrintTwoUp(this.isTwoUp);
    this.pageSetup.updateStylesheet();

    if (this.mode === MODE_SHEET) {
      this.render();
    } else {
      this.setPrint();
    }
  }

  displayError(title, text) {
    if (!this.element.parentNode) {
      document.body.appendChild(this.element);
    }
    if (!this.error) {
      this.zoomBox.innerHTML = '';
      this.error = errorView(title, text);
      this.zoomBox.appendChild(this.error);
    }
  }
  clear() {
    this.book = null;
    this.lastSpreadInProgress = null; // TODO: Make this clearer, after first render
    this.zoomBox.innerHTML = '';
  }
  cancel() {
    // TODO this doesn't work if the target is an existing node
    if (this.element.parentNode) {
      this.element.parentNode.removeChild(this.element);
    }
  }
  toggleGuides() {
    this.element.classList.toggle(c('show-guides'));
  }
  toggleBleed() {
    this.element.classList.add(c('show-bleed'));
  }
  toggleDouble() {
    this.doubleSided = !this.doubleSided;
    this.render();
  }
  setMode(newMode) {
    switch (newMode) {
    case 'grid':
    case 'default':
      this.mode = MODE_PREVIEW;
      break;
    case 'interactive':
    case 'flip':
      this.mode = MODE_FLIP;
      break;
    case 'print':
    case 'sheet':
      this.mode = MODE_SHEET;
      break;
    case 'outline':
    case 'outlines':
      this.mode = MODE_OUTLINE;
      break;
    default:
      console.error(`Bindery: Unknown view mode "${newMode}"`);
      break;
    }
  }
  setGrid() {
    if (this.mode === MODE_PREVIEW) return;
    if (this.mode === MODE_OUTLINE) {
      this.mode = MODE_PREVIEW;
      this.updateGuides();
    } else {
      this.mode = MODE_PREVIEW;
      this.render();
    }
  }
  setOutline() {
    if (this.mode === MODE_OUTLINE) return;
    if (this.mode === MODE_PREVIEW) {
      this.mode = MODE_OUTLINE;
      this.updateGuides();
    } else {
      this.mode = MODE_OUTLINE;
      this.render();
    }
  }
  setPrint() {
    if (this.mode === MODE_SHEET) return;
    this.mode = MODE_SHEET;
    this.render();
  }
  setFlip() {
    this.mode = MODE_FLIP;
    this.render();
  }
  render() {
    if (!this.book) return;
    const { body } = document;

    if (!this.element.parentNode) {
      body.appendChild(this.element);
    }

    this.flaps = [];
    body.classList.add(c('viewing'));
    this.element.setAttribute('bindery-view-mode', this.mode);

    const scrollMax = body.scrollHeight - body.offsetHeight;
    const scrollPct = body.scrollTop / scrollMax;

    if (this.mode === MODE_PREVIEW) {
      this.renderGrid();
    } else if (this.mode === MODE_OUTLINE) {
      this.renderGrid();
    } else if (this.mode === MODE_FLIP) {
      this.renderInteractive();
    } else if (this.mode === MODE_SHEET) {
      this.renderPrint();
    } else {
      this.renderGrid();
    }

    body.scrollTop = scrollMax * scrollPct;
    this.updateZoom();
  }

  renderProgress() {
    const twoPageSpread = function (...arg) {
      return h(c('.spread-wrapper') + c('.spread-size'), ...arg);
    };

    this.book.pages.forEach((page, i) => {
      // If hasn't been added, or not in spread yet
      if (!this.zoomBox.contains(page.element) || page.element.parentNode === this.zoomBox) {
        if (this.lastSpreadInProgress && this.lastSpreadInProgress.children.length < 2) {
          this.lastSpreadInProgress.appendChild(page.element);
        } else {
          if (i === 0) {
            const spacer = new Page();
            spacer.element.style.visibility = 'hidden';
            this.lastSpreadInProgress = twoPageSpread(spacer.element, page.element);
          } else {
            this.lastSpreadInProgress = twoPageSpread(page.element);
          }
          this.zoomBox.appendChild(this.lastSpreadInProgress);
        }
      }
    });

    if (this.book.pageInProgress) {
      this.zoomBox.appendChild(this.book.pageInProgress.element);
    }

    this.updateZoom();
  }

  updateZoom() {
    if (this.zoomBox.firstElementChild) {
      const scrollPct = document.body.scrollTop / document.body.scrollHeight;
      const exportW = this.zoomBox.getBoundingClientRect().width;
      const contentW = this.zoomBox.firstElementChild.getBoundingClientRect().width;

      const scale = Math.min(1, exportW / (contentW + 20));

      this.zoomBox.style.transform = `scale(${scale})`;
      document.body.scrollTop = document.body.scrollHeight * scrollPct;
    }
  }

  updateGuides() {
    this.element.setAttribute('bindery-view-mode', this.mode);
    if (this.mode === MODE_OUTLINE) {
      this.element.classList.add(c('show-bleed'));
      this.element.classList.add(c('show-guides'));
    } else {
      this.element.classList.remove(c('show-bleed'));
      this.element.classList.remove(c('show-guides'));
    }
  }

  renderPrint() {
    this.element.classList.add(c('show-bleed'));
    this.element.classList.remove(c('show-guides'));

    this.zoomBox.innerHTML = '';

    const isBooklet = this.printArrange === ARRANGE_BOOKLET;
    const orient = this.orientation;

    let pages = this.book.pages.slice();
    if (this.printArrange === ARRANGE_SPREAD) {
      pages = padPages(pages);
    } else if (isBooklet) {
      pages = orderPagesBooklet(pages);
    }

    const fragment = printLayout(pages, this.isTwoUp, orient, isBooklet);
    this.zoomBox.appendChild(fragment);
  }

  renderGrid() {
    this.updateGuides();
    this.zoomBox.innerHTML = '';

    let pages = this.book.pages.slice();

    if (this.doubleSided) pages = padPages(pages);

    const fragment = gridLayout(pages, this.doubleSided);
    this.zoomBox.appendChild(fragment);
  }

  renderInteractive() {
    this.zoomBox.innerHTML = '';
    this.flaps = [];

    this.element.classList.remove(c('show-bleed'));
    this.element.classList.remove(c('show-guides'));

    const pages = padPages(this.book.pages.slice());

    const fragment = flipLayout(pages, this.doubleSided);
    this.zoomBox.appendChild(fragment);
  }

}

export default Viewer;
<|endoftext|>


<commit_before>var chai = require('chai');
var expect = chai.expect;
var _ = require('..');

describe('map()', function() {
});
<commit_msg>Renamed thing.<commit_after>var chai = require('chai');
var expect = chai.expect;
var arrayFunctions = require('..');

describe('map()', function() {
});
<|endoftext|>


<commit_before>import { graphql } from 'react-apollo'
import gql from 'graphql-tag'
import { intersection, get } from 'lodash';

export const getLoggedInUserQuery = gql`
  query LoggedInUser {
    LoggedInUser {
      id
      username
      firstName
      lastName
      email
      image
      CollectiveId
      collective {
        id
        name
        type
        paymentMethods {
          id
          uuid
          service
          name
          data
        }
      }
      memberOf {
        id
        role
        collective {
          id
          slug
          type
          name
          currency
          stats {
            id
            balance
            expenses {
              id
              pending
            }
          }
          paymentMethods {
            id
            uuid
            name
            service
            data
            balance
          }
        }
      }
    }
  }
`;

const getTiersQuery = gql`
  query Collective($slug: String!) {
    Collective(slug: $slug) {
      id
      slug
      name
      image
      backgroundImage
      twitterHandle
      description
      currency
      settings
      tiers {
        id
        type
        name
        description
        amount
        currency
        interval
      }
    }
  }
`;

const getCollectiveToEditQuery = gql`
  query Collective($slug: String!) {
    Collective(slug: $slug) {
      id
      type
      slug
      createdByUser {
        id
      }
      name
      image
      backgroundImage
      description
      longDescription
      twitterHandle
      website
      currency
      settings
      createdAt
      stats {
        id
        yearlyBudget
        backers {
          all
        }
        totalAmountSent
      }
      tiers {
        id
        slug
        type
        name
        description
        amount
        presets
        interval
        currency
        maxQuantity
      }
      memberOf {
        id
        createdAt
        role
        stats {
          totalDonations
        }
        tier {
          id
          name
        }
        collective {
          id
          type
          slug
          name
          currency
          description
          settings
          image
          stats {
            id
            backers {
              all
            }
            yearlyBudget
          }
        }
      }
      members(roles: ["ADMIN", "MEMBER"]) {
        id
        createdAt
        role
        description
        stats {
          totalDonations
        }
        tier {
          id
          name
        }
        member {
          id
          name
          image
          slug
          twitterHandle
          description
          ... on User {
            email
          }
        }
      }
      paymentMethods(service: "stripe") {
        id
        uuid
        name
        data
        monthlyLimitPerMember
        orders(hasActiveSubscription: true) {
          id
        }
      }
      connectedAccounts {
        id
        service
        username
        createdAt
      }
    }
  }
`;

const getCollectiveQuery = gql`
  query Collective($slug: String!) {
    Collective(slug: $slug) {
      id
      isActive
      type
      slug
      createdByUser {
        id
      }
      name
      image
      backgroundImage
      description
      longDescription
      twitterHandle
      website
      currency
      settings
      createdAt
      stats {
        id
        balance
        yearlyBudget
        backers {
          all
          users
          organizations
          collectives
        }
        collectives
        transactions
        expenses {
          id
          all
        }
        totalAmountSent
      }
      tiers {
        id
        slug
        type
        name
        description
        button
        amount
        presets
        interval
        currency
        maxQuantity
        stats {
          id
          totalOrders
          availableQuantity
        }
        orders(limit: 30) {
          fromCollective {
            id
            slug
            type
            name
            image
          }
        }
      }
      isHost
      canApply
      host {
        id
        slug
        name
        image
      }
      members {
        id
        role
        createdAt
        description
        member {
          id
          description
          name
          slug
          type
          image
        }
      }
      ... on User {
        memberOf(limit: 50) {
          id
          role
          createdAt
          stats {
            totalDonations
          }
          collective {
            id
            name
            currency
            slug
            type
            image
            description
            longDescription
            backgroundImage
          }
        }
      }
    }
  }
`;

const getEventCollectiveQuery = gql`
  query Collective($eventSlug: String!) {
    Collective(slug: $eventSlug) {
      id
      type
      slug
      createdByUser {
        id
      }
      name
      image
      description
      longDescription
      startsAt
      endsAt
      timezone
      currency
      settings
      location {
        name
        address
        lat
        long
      }
      tiers {
        id
        slug
        type
        name
        description
        amount
        currency
        maxQuantity
      }
      parentCollective {
        id
        slug
        name
        mission
        currency
        backgroundImage
        image
        settings
      }
      members {
        id
        createdAt
        role
        member {
          id
          name
          image
          slug
          twitterHandle
          description
        }
      }
      orders {
        id
        createdAt
        quantity
        processedAt
        publicMessage
        fromCollective {
          id
          name
          image
          slug
          twitterHandle
          description
          ... on User {
            email
          }
        }
        tier {
          id
          name
        }
      }
    }
  }
`;

const getCollectiveCoverQuery = gql`
  query CollectiveCover($slug: String!) {
    Collective(slug: $slug) {
      id
      type
      slug
      name
      currency
      backgroundImage
      settings
      image
      isHost
    }
  }
`;

export const getPrepaidCardBalanceQuery = gql`
  query checkPrepaidPaymentMethod($token: String!) {
    prepaidPaymentMethod(token: $token) {
      id,
      name,
      currency,
      balance,
      uuid
    }
  }
`;

export const addCollectiveData = graphql(getCollectiveQuery);
export const addCollectiveCoverData = graphql(getCollectiveCoverQuery);
export const addCollectiveToEditData = graphql(getCollectiveToEditQuery);
export const addEventCollectiveData = graphql(getEventCollectiveQuery);
export const addTiersData = graphql(getTiersQuery);

export const addGetLoggedInUserFunction = (component) => {
  const accessToken = typeof window !== 'undefined' && window.localStorage.getItem('accessToken');
  if (!accessToken) return component;
  return graphql(getLoggedInUserQuery, {
    props: ({ data }) => ({
      data,
      getLoggedInUser: () => {
        if (!window.localStorage.getItem('accessToken')) {
          return Promise.resolve(null);
        }
        return new Promise(async (resolve) => {
            let res;
            try {
              res = await data.refetch();
              if (!res.data || !res.data.LoggedInUser) {
                return resolve(null);
              }
              const LoggedInUser = {...res.data.LoggedInUser};
              if (LoggedInUser && LoggedInUser.memberOf) {
                const roles = {};
                LoggedInUser.memberOf.map(member => {
                  if (!member.collective) return;
                  roles[member.collective.slug] = roles[member.collective.slug] || [];
                  roles[member.collective.slug].push(member.role);
                });
                LoggedInUser.roles = roles;

                /**
                 * CanEditCollective if LoggedInUser is
                 * - creator of the collective
                 * - is admin or host of the collective
                 */
                LoggedInUser.canEditCollective = (collective) => {
                  if (!collective) return false;
                  return (collective.createdByUser && collective.createdByUser.id === LoggedInUser.id)
                  || intersection(LoggedInUser.roles[collective.slug], ['HOST','ADMIN']).length > 0;
                }

                /**
                 * CanApproveExpense if LoggedInUser is:
                 * - admin or host of expense.collective
                 * - admin or host of expense.collective.host
                 */
                LoggedInUser.canApproveExpense = (expense) => {
                  if (!expense) return false;
                  if (expense.collective) {
                    if (intersection(roles[expense.collective.slug], ['HOST', 'ADMIN']).length > 0) return true;
                    const hostSlug = get(expense, 'collective.host.slug');
                    if (intersection(roles[hostSlug], ['HOST', 'ADMIN']).length > 0) return true;
                  }
                  return false;
                }

                /**
                 * CanEditExpense if LoggedInUser is:
                 * - author of the expense and expense.status === 'PENDING'
                 * - can approve expense (admin or host of expense.collective or expense.collective.host)
                 */
                LoggedInUser.canEditExpense = (expense) => {
                  if (!expense) return false;
                  if (expense.fromCollective && expense.fromCollective.id === LoggedInUser.collective.id && expense.status === 'PENDING') return true;
                  return LoggedInUser.canApproveExpense(expense);
                }

                /**
                 * CanPayExpense if LoggedInUser is HOST or ADMIN of the HOST of the collective
                 */
                LoggedInUser.canPayExpense = (expense) => {
                  const hostSlug = get(expense, 'collective.host.slug');
                  if (intersection(roles[hostSlug], ['HOST', 'ADMIN']).length > 0) return true;
                  return false;
                }
              }
              console.log(">>> LoggedInUser", LoggedInUser);
              return resolve(LoggedInUser);
            } catch (e) {
              console.error(">>> getLoggedInUser error:", e);
              return resolve(null);
            }
        });
      }
    })
  })(component);
}<commit_msg>can't edit expense if already paid<commit_after>import { graphql } from 'react-apollo'
import gql from 'graphql-tag'
import { intersection, get } from 'lodash';

export const getLoggedInUserQuery = gql`
  query LoggedInUser {
    LoggedInUser {
      id
      username
      firstName
      lastName
      email
      image
      CollectiveId
      collective {
        id
        name
        type
        paymentMethods {
          id
          uuid
          service
          name
          data
        }
      }
      memberOf {
        id
        role
        collective {
          id
          slug
          type
          name
          currency
          stats {
            id
            balance
            expenses {
              id
              pending
            }
          }
          paymentMethods {
            id
            uuid
            name
            service
            data
            balance
          }
        }
      }
    }
  }
`;

const getTiersQuery = gql`
  query Collective($slug: String!) {
    Collective(slug: $slug) {
      id
      slug
      name
      image
      backgroundImage
      twitterHandle
      description
      currency
      settings
      tiers {
        id
        type
        name
        description
        amount
        currency
        interval
      }
    }
  }
`;

const getCollectiveToEditQuery = gql`
  query Collective($slug: String!) {
    Collective(slug: $slug) {
      id
      type
      slug
      createdByUser {
        id
      }
      name
      image
      backgroundImage
      description
      longDescription
      twitterHandle
      website
      currency
      settings
      createdAt
      stats {
        id
        yearlyBudget
        backers {
          all
        }
        totalAmountSent
      }
      tiers {
        id
        slug
        type
        name
        description
        amount
        presets
        interval
        currency
        maxQuantity
      }
      memberOf {
        id
        createdAt
        role
        stats {
          totalDonations
        }
        tier {
          id
          name
        }
        collective {
          id
          type
          slug
          name
          currency
          description
          settings
          image
          stats {
            id
            backers {
              all
            }
            yearlyBudget
          }
        }
      }
      members(roles: ["ADMIN", "MEMBER"]) {
        id
        createdAt
        role
        description
        stats {
          totalDonations
        }
        tier {
          id
          name
        }
        member {
          id
          name
          image
          slug
          twitterHandle
          description
          ... on User {
            email
          }
        }
      }
      paymentMethods(service: "stripe") {
        id
        uuid
        name
        data
        monthlyLimitPerMember
        orders(hasActiveSubscription: true) {
          id
        }
      }
      connectedAccounts {
        id
        service
        username
        createdAt
      }
    }
  }
`;

const getCollectiveQuery = gql`
  query Collective($slug: String!) {
    Collective(slug: $slug) {
      id
      isActive
      type
      slug
      createdByUser {
        id
      }
      name
      image
      backgroundImage
      description
      longDescription
      twitterHandle
      website
      currency
      settings
      createdAt
      stats {
        id
        balance
        yearlyBudget
        backers {
          all
          users
          organizations
          collectives
        }
        collectives
        transactions
        expenses {
          id
          all
        }
        totalAmountSent
      }
      tiers {
        id
        slug
        type
        name
        description
        button
        amount
        presets
        interval
        currency
        maxQuantity
        stats {
          id
          totalOrders
          availableQuantity
        }
        orders(limit: 30) {
          fromCollective {
            id
            slug
            type
            name
            image
          }
        }
      }
      isHost
      canApply
      host {
        id
        slug
        name
        image
      }
      members {
        id
        role
        createdAt
        description
        member {
          id
          description
          name
          slug
          type
          image
        }
      }
      ... on User {
        memberOf(limit: 50) {
          id
          role
          createdAt
          stats {
            totalDonations
          }
          collective {
            id
            name
            currency
            slug
            type
            image
            description
            longDescription
            backgroundImage
          }
        }
      }
    }
  }
`;

const getEventCollectiveQuery = gql`
  query Collective($eventSlug: String!) {
    Collective(slug: $eventSlug) {
      id
      type
      slug
      createdByUser {
        id
      }
      name
      image
      description
      longDescription
      startsAt
      endsAt
      timezone
      currency
      settings
      location {
        name
        address
        lat
        long
      }
      tiers {
        id
        slug
        type
        name
        description
        amount
        currency
        maxQuantity
      }
      parentCollective {
        id
        slug
        name
        mission
        currency
        backgroundImage
        image
        settings
      }
      members {
        id
        createdAt
        role
        member {
          id
          name
          image
          slug
          twitterHandle
          description
        }
      }
      orders {
        id
        createdAt
        quantity
        processedAt
        publicMessage
        fromCollective {
          id
          name
          image
          slug
          twitterHandle
          description
          ... on User {
            email
          }
        }
        tier {
          id
          name
        }
      }
    }
  }
`;

const getCollectiveCoverQuery = gql`
  query CollectiveCover($slug: String!) {
    Collective(slug: $slug) {
      id
      type
      slug
      name
      currency
      backgroundImage
      settings
      image
      isHost
    }
  }
`;

export const getPrepaidCardBalanceQuery = gql`
  query checkPrepaidPaymentMethod($token: String!) {
    prepaidPaymentMethod(token: $token) {
      id,
      name,
      currency,
      balance,
      uuid
    }
  }
`;

export const addCollectiveData = graphql(getCollectiveQuery);
export const addCollectiveCoverData = graphql(getCollectiveCoverQuery);
export const addCollectiveToEditData = graphql(getCollectiveToEditQuery);
export const addEventCollectiveData = graphql(getEventCollectiveQuery);
export const addTiersData = graphql(getTiersQuery);

export const addGetLoggedInUserFunction = (component) => {
  const accessToken = typeof window !== 'undefined' && window.localStorage.getItem('accessToken');
  if (!accessToken) return component;
  return graphql(getLoggedInUserQuery, {
    props: ({ data }) => ({
      data,
      getLoggedInUser: () => {
        if (!window.localStorage.getItem('accessToken')) {
          return Promise.resolve(null);
        }
        return new Promise(async (resolve) => {
            let res;
            try {
              res = await data.refetch();
              if (!res.data || !res.data.LoggedInUser) {
                return resolve(null);
              }
              const LoggedInUser = {...res.data.LoggedInUser};
              if (LoggedInUser && LoggedInUser.memberOf) {
                const roles = {};
                LoggedInUser.memberOf.map(member => {
                  if (!member.collective) return;
                  roles[member.collective.slug] = roles[member.collective.slug] || [];
                  roles[member.collective.slug].push(member.role);
                });
                LoggedInUser.roles = roles;

                /**
                 * CanEditCollective if LoggedInUser is
                 * - creator of the collective
                 * - is admin or host of the collective
                 */
                LoggedInUser.canEditCollective = (collective) => {
                  if (!collective) return false;
                  return (collective.createdByUser && collective.createdByUser.id === LoggedInUser.id)
                  || intersection(LoggedInUser.roles[collective.slug], ['HOST','ADMIN']).length > 0;
                }

                /**
                 * CanApproveExpense if LoggedInUser is:
                 * - admin or host of expense.collective
                 * - admin or host of expense.collective.host
                 */
                LoggedInUser.canApproveExpense = (expense) => {
                  if (!expense) return false;
                  if (expense.collective) {
                    if (intersection(roles[expense.collective.slug], ['HOST', 'ADMIN']).length > 0) return true;
                    const hostSlug = get(expense, 'collective.host.slug');
                    if (intersection(roles[hostSlug], ['HOST', 'ADMIN']).length > 0) return true;
                  }
                  return false;
                }

                /**
                 * CanEditExpense if not paid yet and LoggedInUser is:
                 * - author of the expense and expense.status === 'PENDING'
                 * - can approve expense (admin or host of expense.collective or expense.collective.host)
                 */
                LoggedInUser.canEditExpense = (expense) => {
                  if (!expense) return false;
                  if (expense.status === 'PAID') return false;
                  if ( expense.status === 'PENDING' && expense.fromCollective && expense.fromCollective.id === LoggedInUser.collective.id) return true;
                  return LoggedInUser.canApproveExpense(expense);
                }

                /**
                 * CanPayExpense if LoggedInUser is HOST or ADMIN of the HOST of the collective
                 */
                LoggedInUser.canPayExpense = (expense) => {
                  const hostSlug = get(expense, 'collective.host.slug');
                  if (intersection(roles[hostSlug], ['HOST', 'ADMIN']).length > 0) return true;
                  return false;
                }
              }
              console.log(">>> LoggedInUser", LoggedInUser);
              return resolve(LoggedInUser);
            } catch (e) {
              console.error(">>> getLoggedInUser error:", e);
              return resolve(null);
            }
        });
      }
    })
  })(component);
}<|endoftext|>