<commit_before>'use strict';

/*!
 * V4Fire Client Core
 * https://github.com/V4Fire/Client
 *
 * Released under the MIT license
 * https://github.com/V4Fire/Client/blob/master/LICENSE
 */

const
	$C = require('collection.js'),
	fs = require('fs-extra-promise'),
	path = require('upath'),
	camelize = require('camelize');

const
	{build, src} = require('config'),
	{resolve, entries, block} = require('@pzlr/build-core'),
	{output, buildCache} = include('build/build.webpack');

const
	isFastBuild = build.fast();

const
	STD = 0,
	RUNTIME = 1,
	HTML = 2,
	I = [STD, RUNTIME, HTML].length;

const
	RCPU = require('os').cpus().length,
	IN_PROCESS = ['js', 'css', 'html'].length;

let MAX_PROCESS = RCPU * IN_PROCESS;
MAX_PROCESS += MAX_PROCESS <= I ? 1 : 0;

/**
 * Tree of dependencies
 * @type {Promise<{entry, processes, dependencies, blockMap}>}
 */
module.exports = (async () => {
	const
		cacheFile = path.join(buildCache, 'graph.json');

	if (build.buildGraphFromCache) {
		if (fs.existsSync(cacheFile)) {
			await new Promise((r) => {
				const f = () => {
					setTimeout(() => {
						if (fs.readFileSync(cacheFile, 'utf-8') !== '') {
							r();

						} else {
							f();
						}

					}, 15);
				};

				f();
			});

			return fs.readJSONSync(cacheFile, {
				reviver(k, v) {
					if (Object.isObject(v) && v.type === 'Map') {
						return new Map(v.value);
					}

					return v;
				}
			});
		}

	} else {
		fs.mkdirpSync(buildCache);
		fs.writeFileSync(cacheFile, '');
		process.env.BUILD_GRAPH_FROM_CACHE = 1;
	}

	const
		tmpEntries = path.join(resolve.entry(), 'tmp');

	fs.mkdirpSync(tmpEntries);
	fs.mkdirpSync(path.join(src.clientOutput(), path.dirname(output)));

	let
		entriesFilter;

	if (build.entries) {
		entriesFilter = $C(build.entries).reduce((map, el) => (map[el] = true, map), {});
		entriesFilter.index = true;
		entriesFilter.std = true;
	}

	const
		buildConfig = (await entries.getBuildConfig()).filter((el, key) => entriesFilter ? entriesFilter[key] : true),
		blockMap = await block.getAll(),
		graph = await buildConfig.getUnionEntryPoints({cache: blockMap}),
		processes = $C(I).map(() => ({}));

	/**
	 * Returns an url relative to the entry folder
	 */
	function getUrl(url) {
		if (resolve.isNodeModule(url)) {
			return path.normalize(url);
		}

		return path.relative(tmpEntries, url);
	}

	const entry = await $C(graph.entry)
		.parallel()
		.to({})
		.reduce(async (entry, list, name) => {
			// JS / TS

			const
				blackName = /^[iv]-/,
				logicTaskName = `${name}.js`,
				logicFile = path.join(tmpEntries, logicTaskName);

			fs.writeFileSync(logicFile, await $C(list).async.to('').reduce(async (str, {name}) => {
				const
					block = blockMap.get(name),
					logic = block && await block.logic;

				if (block) {
					$C(block.libs).forEach((el) => str += `require('${el}');\n`);
				}

				if (!block || logic) {
					const url = logic ? logic : resolve.isNodeModule(name) ? name : path.resolve(tmpEntries, '../', name);
					str += `require('${getUrl(url)}');\n`;
				}

				return str;
			}));

			entry[logicTaskName] = logicFile;

			if (name === 'std') {
				processes[STD][logicTaskName] = logicFile;

			} else {
				processes[RUNTIME][logicTaskName] = logicFile;
			}

			// CSS

			const
				styleTaskName = `${name}$style`,
				styleFile = path.join(tmpEntries, `${name}.styl`);

			fs.writeFileSync(styleFile, [
				await $C(list).async.to('').reduce(async (str, {name, isParent}) => {
					const
						block = blockMap.get(name),
						style = block && await block.styles;

					if (!isParent && style && style.length && !blackName.test(name)) {
						$C(style).forEach((url) => {
							str += `@import "${getUrl(url)}"\n`;
						});

						if (/^[bp]-/.test(name)) {
							str +=
							`
.${name}
	extends($${camelize(name)})

`;
						}
					}

					return str;
				}),

				'generateImgClasses()'
			].join('\n'));

			let
				union = processes[processes.length - 1];

			if (processes.length === I || MAX_PROCESS > processes.length && $C(union).length() > IN_PROCESS) {
				processes.push(union = {});
			}

			entry[styleTaskName] = union[styleTaskName] = styleFile;

			// TEMPLATES

			const
				tplTaskName = `${name}_tpl.js`,
				tplFile = path.join(tmpEntries, `${name}.ss${!isFastBuild ? '.js' : ''}`);

			fs.writeFileSync(tplFile, await $C(list)
				.async
				.to(isFastBuild ? '' : 'window.TPLS = window.TPLS || Object.create(null);\n')
				.reduce(async (str, {name, isParent}) => {
					const
						block = blockMap.get(name),
						tpl = block && await block.tpl;

					if (!isParent && tpl && !blackName.test(name)) {
						const url = getUrl(tpl);
						str += isFastBuild ? `- include '${url}'\n` : `Object.assign(TPLS, require('./${url}'));\n`;
					}

					return str;
				})
			);

			if (isFastBuild) {
				const
					tplRequireFileUrl = path.join(tmpEntries, tplTaskName);

				fs.writeFileSync(
					tplRequireFileUrl,
					`Object.assign(window.TPLS = window.TPLS || Object.create(null), require('./${getUrl(tplFile)}'));\n`
				);

				entry[tplTaskName] = union[tplTaskName] = tplRequireFileUrl;

			} else {
				entry[tplTaskName] = union[tplTaskName] = tplFile;
			}

			// HTML

			const
				htmlTaskName = `${name}_view`,
				htmlFile = path.join(tmpEntries, `${htmlTaskName}.html.js`);

			fs.writeFileSync(htmlFile, await $C(list).async.to('').reduce(async (str, {name}) => {
				const
					block = blockMap.get(name),
					html = block && await block.etpl;

				if (html && !blackName.test(name)) {
					str += `require('./${getUrl(html)}');\n`;
				}

				return str;
			}));

			entry[htmlTaskName] = processes[HTML][htmlTaskName] = htmlFile;
			return entry;
		});

	processes.push(processes[HTML]);
	processes.splice(HTML, 1);

	$C(processes)
		.remove((obj, i) => i >= I.length && !$C(obj).length());

	blockMap.toJSON = function () {
		return {
			type: 'Map',
			value: Array.from(blockMap.entries())
		};
	};

	const res = {
		entry,
		blockMap,
		processes,
		dependencies: $C(graph.dependencies).map((el, key) => [...el, key])
	};

	fs.writeFileSync(cacheFile, JSON.stringify(res, null, 2));
	console.log('Project graph initialized');

	return res;
})();

Object.assign(module.exports, {
	STD,
	RUNTIME,
	HTML,
	MAX_PROCESS,
});
<commit_msg>New limit for max process<commit_after>'use strict';

/*!
 * V4Fire Client Core
 * https://github.com/V4Fire/Client
 *
 * Released under the MIT license
 * https://github.com/V4Fire/Client/blob/master/LICENSE
 */

const
	$C = require('collection.js'),
	fs = require('fs-extra-promise'),
	path = require('upath'),
	camelize = require('camelize');

const
	{build, src} = require('config'),
	{resolve, entries, block} = require('@pzlr/build-core'),
	{output, buildCache} = include('build/build.webpack');

const
	isFastBuild = build.fast();

const
	STD = 0,
	RUNTIME = 1,
	HTML = 2,
	I = [STD, RUNTIME, HTML].length;

const
	RCPU = require('os').cpus().length,
	IN_PROCESS = ['js', 'css', 'html'].length;

let MAX_PROCESS = RCPU > IN_PROCESS ? RCPU : IN_PROCESS;
MAX_PROCESS += MAX_PROCESS <= I ? 1 : 0;

/**
 * Tree of dependencies
 * @type {Promise<{entry, processes, dependencies, blockMap}>}
 */
module.exports = (async () => {
	const
		cacheFile = path.join(buildCache, 'graph.json');

	if (build.buildGraphFromCache) {
		if (fs.existsSync(cacheFile)) {
			await new Promise((r) => {
				const f = () => {
					setTimeout(() => {
						if (fs.readFileSync(cacheFile, 'utf-8') !== '') {
							r();

						} else {
							f();
						}

					}, 15);
				};

				f();
			});

			return fs.readJSONSync(cacheFile, {
				reviver(k, v) {
					if (Object.isObject(v) && v.type === 'Map') {
						return new Map(v.value);
					}

					return v;
				}
			});
		}

	} else {
		fs.mkdirpSync(buildCache);
		fs.writeFileSync(cacheFile, '');
		process.env.BUILD_GRAPH_FROM_CACHE = 1;
	}

	const
		tmpEntries = path.join(resolve.entry(), 'tmp');

	fs.mkdirpSync(tmpEntries);
	fs.mkdirpSync(path.join(src.clientOutput(), path.dirname(output)));

	let
		entriesFilter;

	if (build.entries) {
		entriesFilter = $C(build.entries).reduce((map, el) => (map[el] = true, map), {});
		entriesFilter.index = true;
		entriesFilter.std = true;
	}

	const
		buildConfig = (await entries.getBuildConfig()).filter((el, key) => entriesFilter ? entriesFilter[key] : true),
		blockMap = await block.getAll(),
		graph = await buildConfig.getUnionEntryPoints({cache: blockMap}),
		processes = $C(I).map(() => ({}));

	/**
	 * Returns an url relative to the entry folder
	 */
	function getUrl(url) {
		if (resolve.isNodeModule(url)) {
			return path.normalize(url);
		}

		return path.relative(tmpEntries, url);
	}

	const entry = await $C(graph.entry)
		.parallel()
		.to({})
		.reduce(async (entry, list, name) => {
			// JS / TS

			const
				blackName = /^[iv]-/,
				logicTaskName = `${name}.js`,
				logicFile = path.join(tmpEntries, logicTaskName);

			fs.writeFileSync(logicFile, await $C(list).async.to('').reduce(async (str, {name}) => {
				const
					block = blockMap.get(name),
					logic = block && await block.logic;

				if (block) {
					$C(block.libs).forEach((el) => str += `require('${el}');\n`);
				}

				if (!block || logic) {
					const url = logic ? logic : resolve.isNodeModule(name) ? name : path.resolve(tmpEntries, '../', name);
					str += `require('${getUrl(url)}');\n`;
				}

				return str;
			}));

			entry[logicTaskName] = logicFile;

			if (name === 'std') {
				processes[STD][logicTaskName] = logicFile;

			} else {
				processes[RUNTIME][logicTaskName] = logicFile;
			}

			// CSS

			const
				styleTaskName = `${name}$style`,
				styleFile = path.join(tmpEntries, `${name}.styl`);

			fs.writeFileSync(styleFile, [
				await $C(list).async.to('').reduce(async (str, {name, isParent}) => {
					const
						block = blockMap.get(name),
						style = block && await block.styles;

					if (!isParent && style && style.length && !blackName.test(name)) {
						$C(style).forEach((url) => {
							str += `@import "${getUrl(url)}"\n`;
						});

						if (/^[bp]-/.test(name)) {
							str +=
							`
.${name}
	extends($${camelize(name)})

`;
						}
					}

					return str;
				}),

				'generateImgClasses()'
			].join('\n'));

			let
				union = processes[processes.length - 1];

			if (processes.length === I || MAX_PROCESS > processes.length && $C(union).length() > IN_PROCESS) {
				processes.push(union = {});
			}

			entry[styleTaskName] = union[styleTaskName] = styleFile;

			// TEMPLATES

			const
				tplTaskName = `${name}_tpl.js`,
				tplFile = path.join(tmpEntries, `${name}.ss${!isFastBuild ? '.js' : ''}`);

			fs.writeFileSync(tplFile, await $C(list)
				.async
				.to(isFastBuild ? '' : 'window.TPLS = window.TPLS || Object.create(null);\n')
				.reduce(async (str, {name, isParent}) => {
					const
						block = blockMap.get(name),
						tpl = block && await block.tpl;

					if (!isParent && tpl && !blackName.test(name)) {
						const url = getUrl(tpl);
						str += isFastBuild ? `- include '${url}'\n` : `Object.assign(TPLS, require('./${url}'));\n`;
					}

					return str;
				})
			);

			if (isFastBuild) {
				const
					tplRequireFileUrl = path.join(tmpEntries, tplTaskName);

				fs.writeFileSync(
					tplRequireFileUrl,
					`Object.assign(window.TPLS = window.TPLS || Object.create(null), require('./${getUrl(tplFile)}'));\n`
				);

				entry[tplTaskName] = union[tplTaskName] = tplRequireFileUrl;

			} else {
				entry[tplTaskName] = union[tplTaskName] = tplFile;
			}

			// HTML

			const
				htmlTaskName = `${name}_view`,
				htmlFile = path.join(tmpEntries, `${htmlTaskName}.html.js`);

			fs.writeFileSync(htmlFile, await $C(list).async.to('').reduce(async (str, {name}) => {
				const
					block = blockMap.get(name),
					html = block && await block.etpl;

				if (html && !blackName.test(name)) {
					str += `require('./${getUrl(html)}');\n`;
				}

				return str;
			}));

			entry[htmlTaskName] = processes[HTML][htmlTaskName] = htmlFile;
			return entry;
		});

	processes.push(processes[HTML]);
	processes.splice(HTML, 1);

	$C(processes)
		.remove((obj, i) => i >= I.length && !$C(obj).length());

	blockMap.toJSON = function () {
		return {
			type: 'Map',
			value: Array.from(blockMap.entries())
		};
	};

	const res = {
		entry,
		blockMap,
		processes,
		dependencies: $C(graph.dependencies).map((el, key) => [...el, key])
	};

	fs.writeFileSync(cacheFile, JSON.stringify(res, null, 2));
	console.log('Project graph initialized');

	return res;
})();

Object.assign(module.exports, {
	STD,
	RUNTIME,
	HTML,
	MAX_PROCESS,
});
<|endoftext|>


<commit_before><commit_msg>fix formatting<commit_after><|endoftext|>


<commit_before><commit_msg>finished overhaul to allow use of jwplayer<commit_after><|endoftext|>


<commit_before>const assert = require('assert');
const nock = require('nock');
const sinon = require('sinon');

// module under test
const MailgunClient = require('../');

// Some sample Mailgun API options we might want to use
const MAILGUN_OPTIONS = {
    event: 'delivered OR opened OR failed OR unsubscribed OR complained',
    limit: 300,
    tags: 'bulk-email'
};

describe('MailgunClient', function () {
    let config, settings;

    beforeEach(function () {
        // options objects that can be stubbed or spied
        config = {get() {}};
        settings = {get() {}};
    });

    afterEach(function () {
        sinon.restore();
    });

    it('can connect via config', function () {
        const configStub = sinon.stub(config, 'get');
        configStub.withArgs('bulkEmail').returns({
            mailgun: {
                apiKey: 'apiKey',
                domain: 'domain.com',
                baseUrl: 'https://api.mailgun.net/v3'
            }
        });

        const mailgunClient = new MailgunClient({config, settings});
        assert.equal(mailgunClient.isConfigured(), true);
    });

    it('can connect via settings', function () {
        const settingsStub = sinon.stub(settings, 'get');
        settingsStub.withArgs('mailgun_api_key').returns('settingsApiKey');
        settingsStub.withArgs('mailgun_domain').returns('settingsdomain.com');
        settingsStub.withArgs('mailgun_base_url').returns('https://example.com/v3');

        const mailgunClient = new MailgunClient({config, settings});
        assert.equal(mailgunClient.isConfigured(), true);
    });

    it('cannot configure Mailgun if config/settings missing', function () {
        const mailgunClient = new MailgunClient({config, settings});
        assert.equal(mailgunClient.isConfigured(), false);
    });

    it('respects changes in settings', async function () {
        const settingsStub = sinon.stub(settings, 'get');
        settingsStub.withArgs('mailgun_api_key').returns('settingsApiKey');
        settingsStub.withArgs('mailgun_domain').returns('settingsdomain.com');
        settingsStub.withArgs('mailgun_base_url').returns('https://example.com/v3');

        const eventsMock1 = nock('https://example.com')
            .get('/v3/settingsdomain.com/events')
            .query(MAILGUN_OPTIONS)
            .reply(200, {'Content-Type': 'application/json'}, {
                items: []
            });

        const mailgunClient = new MailgunClient({config, settings});
        await mailgunClient.fetchEvents(MAILGUN_OPTIONS, () => {});

        settingsStub.withArgs('mailgun_api_key').returns('settingsApiKey2');
        settingsStub.withArgs('mailgun_domain').returns('settingsdomain2.com');
        settingsStub.withArgs('mailgun_base_url').returns('https://example2.com/v3');

        const eventsMock2 = nock('https://example2.com')
            .get('/v3/settingsdomain2.com/events')
            .query(MAILGUN_OPTIONS)
            .reply(200, {'Content-Type': 'application/json'}, {
                items: []
            });

        await mailgunClient.fetchEvents(MAILGUN_OPTIONS, () => {});

        assert.equal(eventsMock1.isDone(), true);
        assert.equal(eventsMock2.isDone(), true);
    });

    describe('send()', function () {
        it('does not send if not configured', async function () {
            const mailgunClient = new MailgunClient({config, settings});
            const response = await mailgunClient.send({}, {}, []);

            assert.strictEqual(response, null);
        });
    });

    describe('fetchEvents()', function () {
        it('does not fetch if not configured', async function () {
            const batchHandler = sinon.spy();
            const mailgunClient = new MailgunClient({config, settings});
            const events = await mailgunClient.fetchEvents(MAILGUN_OPTIONS, batchHandler);

            assert.equal(events.length, 0);
            assert.equal(batchHandler.callCount, 0);
        });

        it('fetches from now and works backwards', async function () {
            const configStub = sinon.stub(config, 'get');
            configStub.withArgs('bulkEmail').returns({
                mailgun: {
                    apiKey: 'apiKey',
                    domain: 'domain.com',
                    baseUrl: 'https://api.mailgun.net/v3'
                }
            });

            const firstPageMock = nock('https://api.mailgun.net')
                .get('/v3/domain.com/events')
                .query(MAILGUN_OPTIONS)
                .replyWithFile(200, `${__dirname}/fixtures/all-1.json`, {
                    'Content-Type': 'application/json'
                });

            const secondPageMock = nock('https://api.mailgun.net')
                .get('/v3/domain.com/events/all-1-next')
                .replyWithFile(200, `${__dirname}/fixtures/all-2.json`, {
                    'Content-Type': 'application/json'
                });

            // requests continue until an empty items set is returned
            nock('https://api.mailgun.net')
                .get('/v3/domain.com/events/all-2-next')
                .reply(200, {'Content-Type': 'application/json'}, {
                    items: []
                });

            const batchHandler = sinon.spy();

            const mailgunClient = new MailgunClient({config, settings});
            await mailgunClient.fetchEvents(MAILGUN_OPTIONS, batchHandler);

            assert.equal(firstPageMock.isDone(), true);
            assert.equal(secondPageMock.isDone(), true);
            assert.equal(batchHandler.callCount, 2); // one per page
        });

        it('fetches with a limit', async function () {
            const configStub = sinon.stub(config, 'get');
            configStub.withArgs('bulkEmail').returns({
                mailgun: {
                    apiKey: 'apiKey',
                    domain: 'domain.com',
                    baseUrl: 'https://api.mailgun.net/v3'
                }
            });

            const firstPageMock = nock('https://api.mailgun.net')
                .get('/v3/domain.com/events')
                .query(MAILGUN_OPTIONS)
                .replyWithFile(200, `${__dirname}/fixtures/all-1.json`, {
                    'Content-Type': 'application/json'
                });

            const secondPageMock = nock('https://api.mailgun.net')
                .get('/v3/domain.com/events/all-1-next')
                .replyWithFile(200, `${__dirname}/fixtures/all-2.json`, {
                    'Content-Type': 'application/json'
                });

            // requests continue until an empty items set is returned
            nock('https://api.mailgun.net')
                .get('/v3/domain.com/events/all-2-next')
                .reply(200, {'Content-Type': 'application/json'}, {
                    items: []
                });

            const batchHandler = sinon.stub().returnsArg(0);

            const maxEvents = 3;

            const mailgunClient = new MailgunClient({config, settings});
            const events = await mailgunClient.fetchEvents(MAILGUN_OPTIONS, batchHandler, {maxEvents});

            assert.equal(events.length, 4); // `maxEvents` is 3 but the first page contains 4 events
            assert.equal(firstPageMock.isDone(), true);
            assert.equal(secondPageMock.isDone(), false);
            assert.equal(batchHandler.callCount, 1);
        });

        it('supports EU Mailgun domain', async function () {
            const configStub = sinon.stub(config, 'get');
            configStub.withArgs('bulkEmail').returns({
                mailgun: {
                    apiKey: 'apiKey',
                    domain: 'domain.com',
                    baseUrl: 'https://api.eu.mailgun.net/v3'
                }
            });

            const firstPageMock = nock('https://api.eu.mailgun.net')
                .get('/v3/domain.com/events')
                .query(MAILGUN_OPTIONS)
                .replyWithFile(200, `${__dirname}/fixtures/all-1-eu.json`, {
                    'Content-Type': 'application/json'
                });

            const secondPageMock = nock('https://api.eu.mailgun.net')
                .get('/v3/domain.com/events/all-1-next')
                .replyWithFile(200, `${__dirname}/fixtures/all-2-eu.json`, {
                    'Content-Type': 'application/json'
                });

            // requests continue until an empty items set is returned
            nock('https://api.eu.mailgun.net')
                .get('/v3/domain.com/events/all-2-next')
                .reply(200, {'Content-Type': 'application/json'}, {
                    items: []
                });

            const batchHandler = sinon.spy();

            const mailgunClient = new MailgunClient({config, settings});
            await mailgunClient.fetchEvents(MAILGUN_OPTIONS, batchHandler);

            assert.equal(firstPageMock.isDone(), true);
            assert.equal(secondPageMock.isDone(), true);
            assert.equal(batchHandler.callCount, 2); // one per page
        });
    });

    describe('normalizeEvent()', function () {
        it('works', function () {
            const event = {
                event: 'testEvent',
                severity: 'testSeverity',
                recipient: 'testRecipient',
                timestamp: 1614275662,
                message: {
                    headers: {
                        'message-id': 'testProviderId'
                    }
                },
                'user-variables': {
                    'email-id': 'testEmailId'
                }
            };

            const mailgunClient = new MailgunClient({config, settings});
            const result = mailgunClient.normalizeEvent(event);

            assert.deepStrictEqual(result, {
                type: 'testEvent',
                severity: 'testSeverity',
                recipientEmail: 'testRecipient',
                emailId: 'testEmailId',
                providerId: 'testProviderId',
                timestamp: new Date('2021-02-25T17:54:22.000Z')
            });
        });
    });
});
<commit_msg>Added mailgun-client test to ensure config values are prioritised over settings<commit_after>const assert = require('assert');
const nock = require('nock');
const sinon = require('sinon');

// module under test
const MailgunClient = require('../');

// Some sample Mailgun API options we might want to use
const MAILGUN_OPTIONS = {
    event: 'delivered OR opened OR failed OR unsubscribed OR complained',
    limit: 300,
    tags: 'bulk-email'
};

describe('MailgunClient', function () {
    let config, settings;

    beforeEach(function () {
        // options objects that can be stubbed or spied
        config = {get() {}};
        settings = {get() {}};
    });

    afterEach(function () {
        sinon.restore();
    });

    it('can connect via config', function () {
        const configStub = sinon.stub(config, 'get');
        configStub.withArgs('bulkEmail').returns({
            mailgun: {
                apiKey: 'apiKey',
                domain: 'domain.com',
                baseUrl: 'https://api.mailgun.net/v3'
            }
        });

        const mailgunClient = new MailgunClient({config, settings});
        assert.equal(mailgunClient.isConfigured(), true);
    });

    it('can connect via settings', function () {
        const settingsStub = sinon.stub(settings, 'get');
        settingsStub.withArgs('mailgun_api_key').returns('settingsApiKey');
        settingsStub.withArgs('mailgun_domain').returns('settingsdomain.com');
        settingsStub.withArgs('mailgun_base_url').returns('https://example.com/v3');

        const mailgunClient = new MailgunClient({config, settings});
        assert.equal(mailgunClient.isConfigured(), true);
    });

    it('cannot configure Mailgun if config/settings missing', function () {
        const mailgunClient = new MailgunClient({config, settings});
        assert.equal(mailgunClient.isConfigured(), false);
    });

    it('respects changes in settings', async function () {
        const settingsStub = sinon.stub(settings, 'get');
        settingsStub.withArgs('mailgun_api_key').returns('settingsApiKey');
        settingsStub.withArgs('mailgun_domain').returns('settingsdomain.com');
        settingsStub.withArgs('mailgun_base_url').returns('https://example.com/v3');

        const eventsMock1 = nock('https://example.com')
            .get('/v3/settingsdomain.com/events')
            .query(MAILGUN_OPTIONS)
            .reply(200, {'Content-Type': 'application/json'}, {
                items: []
            });

        const mailgunClient = new MailgunClient({config, settings});
        await mailgunClient.fetchEvents(MAILGUN_OPTIONS, () => {});

        settingsStub.withArgs('mailgun_api_key').returns('settingsApiKey2');
        settingsStub.withArgs('mailgun_domain').returns('settingsdomain2.com');
        settingsStub.withArgs('mailgun_base_url').returns('https://example2.com/v3');

        const eventsMock2 = nock('https://example2.com')
            .get('/v3/settingsdomain2.com/events')
            .query(MAILGUN_OPTIONS)
            .reply(200, {'Content-Type': 'application/json'}, {
                items: []
            });

        await mailgunClient.fetchEvents(MAILGUN_OPTIONS, () => {});

        assert.equal(eventsMock1.isDone(), true);
        assert.equal(eventsMock2.isDone(), true);
    });

    it('prioritises config values over settings', async function () {
        const configStub = sinon.stub(config, 'get');
        configStub.withArgs('bulkEmail').returns({
            mailgun: {
                apiKey: 'apiKey',
                domain: 'configdomain.com',
                baseUrl: 'https://configapi.com/v3'
            }
        });

        const settingsStub = sinon.stub(settings, 'get');
        settingsStub.withArgs('mailgun_api_key').returns('settingsApiKey');
        settingsStub.withArgs('mailgun_domain').returns('settingsdomain.com');
        settingsStub.withArgs('mailgun_base_url').returns('https://settingsapi.com/v3');

        const configApiMock = nock('https://configapi.com')
            .get('/v3/configdomain.com/events')
            .query(MAILGUN_OPTIONS)
            .reply(200, {'Content-Type': 'application/json'}, {
                items: []
            });

        const settingsApiMock = nock('https://settingsapi.com')
            .get('/v3/settingsdomain.com/events')
            .query(MAILGUN_OPTIONS)
            .reply(200, {'Content-Type': 'application/json'}, {
                items: []
            });

        const mailgunClient = new MailgunClient({config, settings});
        await mailgunClient.fetchEvents(MAILGUN_OPTIONS, () => {});

        assert.equal(configApiMock.isDone(), true);
        assert.equal(settingsApiMock.isDone(), false);
    });

    describe('send()', function () {
        it('does not send if not configured', async function () {
            const mailgunClient = new MailgunClient({config, settings});
            const response = await mailgunClient.send({}, {}, []);

            assert.strictEqual(response, null);
        });
    });

    describe('fetchEvents()', function () {
        it('does not fetch if not configured', async function () {
            const batchHandler = sinon.spy();
            const mailgunClient = new MailgunClient({config, settings});
            const events = await mailgunClient.fetchEvents(MAILGUN_OPTIONS, batchHandler);

            assert.equal(events.length, 0);
            assert.equal(batchHandler.callCount, 0);
        });

        it('fetches from now and works backwards', async function () {
            const configStub = sinon.stub(config, 'get');
            configStub.withArgs('bulkEmail').returns({
                mailgun: {
                    apiKey: 'apiKey',
                    domain: 'domain.com',
                    baseUrl: 'https://api.mailgun.net/v3'
                }
            });

            const firstPageMock = nock('https://api.mailgun.net')
                .get('/v3/domain.com/events')
                .query(MAILGUN_OPTIONS)
                .replyWithFile(200, `${__dirname}/fixtures/all-1.json`, {
                    'Content-Type': 'application/json'
                });

            const secondPageMock = nock('https://api.mailgun.net')
                .get('/v3/domain.com/events/all-1-next')
                .replyWithFile(200, `${__dirname}/fixtures/all-2.json`, {
                    'Content-Type': 'application/json'
                });

            // requests continue until an empty items set is returned
            nock('https://api.mailgun.net')
                .get('/v3/domain.com/events/all-2-next')
                .reply(200, {'Content-Type': 'application/json'}, {
                    items: []
                });

            const batchHandler = sinon.spy();

            const mailgunClient = new MailgunClient({config, settings});
            await mailgunClient.fetchEvents(MAILGUN_OPTIONS, batchHandler);

            assert.equal(firstPageMock.isDone(), true);
            assert.equal(secondPageMock.isDone(), true);
            assert.equal(batchHandler.callCount, 2); // one per page
        });

        it('fetches with a limit', async function () {
            const configStub = sinon.stub(config, 'get');
            configStub.withArgs('bulkEmail').returns({
                mailgun: {
                    apiKey: 'apiKey',
                    domain: 'domain.com',
                    baseUrl: 'https://api.mailgun.net/v3'
                }
            });

            const firstPageMock = nock('https://api.mailgun.net')
                .get('/v3/domain.com/events')
                .query(MAILGUN_OPTIONS)
                .replyWithFile(200, `${__dirname}/fixtures/all-1.json`, {
                    'Content-Type': 'application/json'
                });

            const secondPageMock = nock('https://api.mailgun.net')
                .get('/v3/domain.com/events/all-1-next')
                .replyWithFile(200, `${__dirname}/fixtures/all-2.json`, {
                    'Content-Type': 'application/json'
                });

            // requests continue until an empty items set is returned
            nock('https://api.mailgun.net')
                .get('/v3/domain.com/events/all-2-next')
                .reply(200, {'Content-Type': 'application/json'}, {
                    items: []
                });

            const batchHandler = sinon.stub().returnsArg(0);

            const maxEvents = 3;

            const mailgunClient = new MailgunClient({config, settings});
            const events = await mailgunClient.fetchEvents(MAILGUN_OPTIONS, batchHandler, {maxEvents});

            assert.equal(events.length, 4); // `maxEvents` is 3 but the first page contains 4 events
            assert.equal(firstPageMock.isDone(), true);
            assert.equal(secondPageMock.isDone(), false);
            assert.equal(batchHandler.callCount, 1);
        });

        it('supports EU Mailgun domain', async function () {
            const configStub = sinon.stub(config, 'get');
            configStub.withArgs('bulkEmail').returns({
                mailgun: {
                    apiKey: 'apiKey',
                    domain: 'domain.com',
                    baseUrl: 'https://api.eu.mailgun.net/v3'
                }
            });

            const firstPageMock = nock('https://api.eu.mailgun.net')
                .get('/v3/domain.com/events')
                .query(MAILGUN_OPTIONS)
                .replyWithFile(200, `${__dirname}/fixtures/all-1-eu.json`, {
                    'Content-Type': 'application/json'
                });

            const secondPageMock = nock('https://api.eu.mailgun.net')
                .get('/v3/domain.com/events/all-1-next')
                .replyWithFile(200, `${__dirname}/fixtures/all-2-eu.json`, {
                    'Content-Type': 'application/json'
                });

            // requests continue until an empty items set is returned
            nock('https://api.eu.mailgun.net')
                .get('/v3/domain.com/events/all-2-next')
                .reply(200, {'Content-Type': 'application/json'}, {
                    items: []
                });

            const batchHandler = sinon.spy();

            const mailgunClient = new MailgunClient({config, settings});
            await mailgunClient.fetchEvents(MAILGUN_OPTIONS, batchHandler);

            assert.equal(firstPageMock.isDone(), true);
            assert.equal(secondPageMock.isDone(), true);
            assert.equal(batchHandler.callCount, 2); // one per page
        });
    });

    describe('normalizeEvent()', function () {
        it('works', function () {
            const event = {
                event: 'testEvent',
                severity: 'testSeverity',
                recipient: 'testRecipient',
                timestamp: 1614275662,
                message: {
                    headers: {
                        'message-id': 'testProviderId'
                    }
                },
                'user-variables': {
                    'email-id': 'testEmailId'
                }
            };

            const mailgunClient = new MailgunClient({config, settings});
            const result = mailgunClient.normalizeEvent(event);

            assert.deepStrictEqual(result, {
                type: 'testEvent',
                severity: 'testSeverity',
                recipientEmail: 'testRecipient',
                emailId: 'testEmailId',
                providerId: 'testProviderId',
                timestamp: new Date('2021-02-25T17:54:22.000Z')
            });
        });
    });
});
<|endoftext|>


<commit_before>'use strict';

var path = require('path');

module.exports = function(grunt){
    grunt.registerMultiTask('browserify', 'Bundle one or more assets through browserify', function(){
        var browserify = require('browserify'),
            modules = browserify(),
            data = this.data,
            src = grunt.file.expand(data.src || []);

        src.forEach(function(){
            modules.add(file);
        });

        grunt.file.write(data.output, modules.bundle());
    });
};<commit_msg>fixed arg<commit_after>'use strict';

var path = require('path');

module.exports = function(grunt){
    grunt.registerMultiTask('browserify', 'Bundle assets through browserify', function(){
        var browserify = require('browserify'),
            modules = browserify(),
            data = this.data,
            src = grunt.file.expand(data.src || []);

        src.forEach(function(file){
            modules.add(file);
        });

        grunt.file.write(data.output, modules.bundle());
    });
};<|endoftext|>


<commit_before>var // Maths
    M = Math,
    phi = .618,
    PHI = 1 / phi,
    RGBMAX = 255,
    ceil = M.ceil,
    random = M.random,
    pow = M.pow,

    // Settings
    fps = PHI,
    unitsPerFrame = phi * 100,
    multiplier = 10,
    maxDriftFactor = PHI / Infinity, // factor of canvas dimensions

    // Window & document
    win = this,
    doc = document,
    canvas = doc.getElementById('c'),
    ctx = canvas.getContext('2d'),
    width = canvas.width = win.innerWidth,
    height = canvas.height = win.innerHeight,

    // Modifiers
    factor = multiplier * phi,
    maxDriftX = width * maxDriftFactor,
    maxDriftY = height * maxDriftFactor,
    maxRadius = width * pow(phi, phi * 10),

    // Timeline
    frequency = 1 / fps,
    intervalRef,

    // String lookups
    length = 'length',
    rgba = 'rgba(',
    stroke = 'stroke',
    strokeStyle = stroke + 'Style',
    beginPath = 'beginPath',
    closePath = 'closePath';

// **

function randomInt(length){
    return ceil((length || 2) * random()) - 1;
}

function drift(maxDrift){
    return ceil(random() * (maxDrift * 2 + 1) - maxDrift / 2 - 1);
}

function circle(x, y, intensity){
    var radius = ceil(intensity * maxRadius),
        r = RGBMAX,
        g = RGBMAX,
        b = RGBMAX,
        rgbStr;

    // create paths
    ctx[beginPath]();
    ctx.arc(x, y, radius, 0, M.PI * 2, 0);
    ctx[closePath]();

    // styles
    //rbgStr = rgba + r + ',' + g + ',' + b + ',';
    //ctx[strokeStyle] = rbgStr + phi * drift +')';
    //ctx.fillStyle = rbgStr + (phi * phi * phi * drift) +')';
    rbgStr = rgba + r + ',' + g + ',' + b + ',';
    ctx[strokeStyle] = rbgStr + intensity +')';
    ctx.fillStyle = rbgStr + (phi * intensity) +')';

    // draw
    ctx[stroke]();
    ctx.fill();
}

function lines(units){ // i === coords.length
    var i = units[length] - 1,
        xy = units[i];

    // draw connecting lines
    ctx[beginPath]();
    ctx.moveTo(xy[0], xy[1]);
    for (; i; i--){
        xy = units[i-1];
        ctx.lineTo(xy[0], xy[1]);
    }
    ctx[closePath]();
}

function unit(){
    var x = randomInt(width - maxDriftX + 1),
        y = randomInt(height - maxDriftY + 1),
        factorX, factorY,
        directionX, directionY,
        driftX, driftY,
        intensity;

    // Randomise
    driftX = drift(maxDriftX);
    driftY = drift(maxDriftY);

    // Calculate new position
    factorX = x * factor;
    factorY = y * factor;
    x = ceil(x + (directionX ? factorX : -factorX) + driftX);
    y = ceil(y + (directionY ? factorY : -factorY) + driftY);

    // Wrap around at boundaries to the canvas width and height
    if (x > width || x < 0){
        x = x > width ?
            2 * width - x :
            0 - x;
        directionX = ~directionX; // swich bits, to reverse the direction
    }
    if (y > height || y < 0){
        y = y > height ?
            2 * height - y :
            0 - y;
        directionY = ~directionY;
    }
    intensity = x / width * (y / height) / 2; // x, y position, in relation to the available width and height
    circle(x, y, intensity);

    return [x, y, intensity];
}

function frame(){
    var units = [];

    for (var i = 0; i < unitsPerFrame; i++){
        units[i] = unit();
    }

    // white lines (don't do it)
    lines(units.slice(0,2));
    ctx[strokeStyle] = rgba + '100,100,100,' + phi * units[0][2] + ')';
    ctx[stroke]();

    // black
    lines(units.slice(2));
    ctx[strokeStyle] = rgba + '0,0,0,' + phi * phi * units[2][2] + ')';
    ctx[stroke]();
}

// Set body style
doc.body.style.cssText = 'margin:0;background-color:#000;overflow:hidden';

//setInterval(frame, frequency);

// toggle animation on any mouse click or key press
(canvas.onclick = doc.onkeydown = function(){
    intervalRef = intervalRef ?
        clearInterval(intervalRef) :    // clearInterval and set intervalRef to undefined
        setInterval(frame, frequency);  // setInterval and create reference to it
})(); // start animation
<commit_msg>intensity value made 0 to 1<commit_after>var // Maths
    M = Math,
    phi = .618,
    PHI = 1 / phi,
    RGBMAX = 255,
    ceil = M.ceil,
    random = M.random,
    pow = M.pow,

    // Settings
    fps = PHI,
    unitsPerFrame = phi * 100,
    multiplier = 1,
    maxDriftFactor = PHI / Infinity, // factor of canvas dimensions

    // Window & document
    win = this,
    doc = document,
    canvas = doc.getElementById('c'),
    ctx = canvas.getContext('2d'),
    width = canvas.width = win.innerWidth,
    height = canvas.height = win.innerHeight,

    // Modifiers
    factor = 1 + (1 / multiplier * phi),
    maxDriftX = width * maxDriftFactor,
    maxDriftY = height * maxDriftFactor,
    maxRadius = width * pow(phi, phi * 16),

    // Timeline
    frequency = 1 / fps,
    intervalRef,

    // String lookups
    length = 'length',
    rgba = 'rgba(',
    stroke = 'stroke',
    strokeStyle = stroke + 'Style',
    beginPath = 'beginPath',
    closePath = 'closePath';

// **

function randomInt(length){
    return ceil((length || 2) * random()) - 1;
}

function drift(maxDrift){
    return ceil(random() * (maxDrift * 2 + 1) - maxDrift / 2 - 1);
}

function circle(x, y, intensity){
    var radius = ceil(intensity * maxRadius),
        r = RGBMAX,
        g = RGBMAX,
        b = RGBMAX,
        rgbStr;

    // create paths
    ctx[beginPath]();
    ctx.arc(x, y, radius, 0, M.PI * 2, 0);
    ctx[closePath]();

    // styles
    //rbgStr = rgba + r + ',' + g + ',' + b + ',';
    //ctx[strokeStyle] = rbgStr + phi * drift +')';
    //ctx.fillStyle = rbgStr + (phi * phi * phi * drift) +')';
    rbgStr = rgba + r + ',' + g + ',' + b + ',';
    ctx[strokeStyle] = rbgStr + (phi * intensity) +')';
    ctx.fillStyle = rbgStr + (phi / PHI * intensity) +')';

    // draw
    ctx[stroke]();
    ctx.fill();
}

function lines(units){ // i === coords.length
    var i = units[length] - 1,
        xy = units[i];

    // draw connecting lines
    ctx[beginPath]();
    ctx.moveTo(xy[0], xy[1]);
    for (; i; i--){
        xy = units[i-1];
        ctx.lineTo(xy[0], xy[1]);
    }
    ctx[closePath]();
}

function unit(){
    var x = randomInt(width - maxDriftX + 1),
        y = randomInt(height - maxDriftY + 1),
        factorX, factorY,
        directionX, directionY,
        driftX, driftY,
        intensity;

    // Randomise
    driftX = drift(maxDriftX);
    driftY = drift(maxDriftY);

    // Calculate new position
    factorX = x * factor;
    factorY = y * factor;

    x = ceil(x + (directionX ? factorX : -factorX) + driftX);
    y = ceil(y + (directionY ? factorY : -factorY) + driftY);

    // Wrap around at boundaries to the canvas width and height
    if (x > width || x < 0){
        x = x > width ?
            2 * width - x :
            0 - x;
        directionX = ~directionX; // swich bits, to reverse the direction
    }
    if (y > height || y < 0){
        y = y > height ?
            2 * height - y :
            0 - y;
        directionY = ~directionY;
    }
    x = x * PHI;
    y = y * PHI;

    intensity = ((x / width) + (y / height)) / 2; // x, y position, in relation to the available width and height
    _(x, y, intensity);
    circle(x, y, intensity);

    return [x, y, intensity];
}

function frame(){
    var units = [];

    for (var i = 0; i < unitsPerFrame; i++){
        units[i] = unit();
    }

    // white lines (don't do it)
    lines(units.slice(0,2));
    ctx[strokeStyle] = rgba + '100,100,100,' + phi * units[0][2] + ')';
    ctx[stroke]();

    /*
    if (!randomInt(3)){
        window.setTimeout(function(){
            lines(units.slice(0,2));
            ctx[strokeStyle] = rgba + '0,0,0,' + units[0][2] + ')';
            ctx[stroke]();
        }, 1000);
    }
    */

    // black
    lines(units.slice(2));
    ctx[strokeStyle] = rgba + '0,0,0,' + phi * units[2][2] + ')';
    ctx[stroke]();
}

// Set body style
doc.body.style.cssText = 'margin:0;background-color:#000;overflow:hidden';

//setInterval(frame, frequency);

// toggle animation on any mouse click or key press
(canvas.onclick = doc.onkeydown = function(){
    intervalRef = intervalRef ?
        clearInterval(intervalRef) :    // clearInterval and set intervalRef to undefined
        setInterval(frame, frequency);  // setInterval and create reference to it
})(); // start animation
<|endoftext|>


<commit_before>/*
Hormigas version 5
Copyright (c) 2013, Peter Michaux
All rights reserved.
Licensed under the Simplified BSD License.
https://github.com/petermichaux/hormigas/blob/master/LICENSE
*/
<commit_msg>update version<commit_after>/*
Hormigas version 1.0.4
Copyright (c) 2013, Peter Michaux
All rights reserved.
Licensed under the Simplified BSD License.
https://github.com/petermichaux/hormigas/blob/master/LICENSE
*/
<|endoftext|>


<commit_before>var $ = require('jquery');
var bootbox = require('bootbox');
var $osf = require('osfHelpers');

    $(document).ready(function() {

        $('#figshareAddKey').on('click', function() {
            if ($(this)[0].outerText == 'Authorize: Create Access Token')
                window.location.href = nodeApiUrl + 'figshare/oauth/';
            $.ajax({
                type: 'POST',
                url: nodeApiUrl + 'figshare/user_auth/',
                contentType: 'application/json',
                dataType: 'json',
                success: function(response) {
                    window.location.reload();
                }
            });

        });

        $('#figshareDelKey').on('click', function() {
            bootbox.confirm({
                title: 'Remove access key?',
                message: 'Are you sure you want to remove your figshare access key? This will ' +
                'revoke the ability to modify and upload files to figshare. If ' +
                'the associated repo is private, this will also disable viewing ' +
                'and downloading files from figshare.',
                callback: function(result) {
                    if(result) {
                        $.ajax({
                            url: nodeApiUrl + 'figshare/oauth/',
                            type: 'DELETE',
                            contentType: 'application/json',
                            dataType: 'json',
                            success: function() {
                                window.location.reload();
                            }
                        });
                    }
                },
                buttons:{
                    confirm:{
                        label:'Remove',
                        className:'btn-danger'
                    }
                }
            });
        });

        $('#figshareSelectProject').on('change', function() {
            var value = $(this).val();
            if (value) {
                $('#figshareId').val(value)
                $('#figshareTitle').val($('#figshareSelectProject option:selected').text())
            }
        });

        $('#addonSettingsFigshare .addon-settings-submit').on('click', function() {
            if ($('#figshareId').val() == '-----') {
                return false;
            }
        });

    });
<commit_msg>Remove dead code<commit_after><|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';

// Redux
import { connect } from 'react-redux';

// Services
import UserService from 'services/UserService';
import DatasetService from 'services/DatasetService';

// Components
import Spinner from 'components/ui/Spinner';
import DatasetList from 'components/app/explore/DatasetList';

class MyRWDatasetsStarred extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      user: null,
      starredDatasets: [],
      starredDatasetsLoaded: null
    };

    // User service
    this.userService = new UserService({ apiURL: process.env.CONTROL_TOWER_URL });
  }

  componentDidMount() {
    this.loadDatasets();
  }

  loadDatasets() {
    const { user } = this.props;

    const favourites = user.favourites;
    const datasetIds = favourites.filter(f => f.attributes.resourceType === 'dataset').map(elem => elem.attributes.resourceId);

    if (datasetIds.length) {
      DatasetService.getDatasets(datasetIds, this.props.locale, 'widget,layer,vocabulary,metadata')
        .then((resp) => {
          this.setState({
            starredDatasets: resp,
            starredDatasetsLoaded: true
          });
        })
        .catch((err) => {
          console.error(err);
        });
    } else {
      this.setState({
        starredDatasetsLoaded: true
      });
    }
  }

  render() {
    const { user } = this.props;
    const { starredDatasets, starredDatasetsLoaded } = this.state;
    return (

            <Spinner
              isLoading={!starredDatasetsLoaded}
              className="-relative -light"
            />

            {starredDatasets &&
              <DatasetList
                list={starredDatasets}
                favourites={user.favourites}
                mode="grid"
                showActions={false}
                showFavorite
              />
            }

            {starredDatasetsLoaded === false && starredDatasets && starredDatasets.length === 0 &&

                You currently have no starred datasets

            }

    );
  }
}

MyRWDatasetsStarred.propTypes = {
  // Store
  user: PropTypes.object.isRequired,
  locale: PropTypes.string.isRequired
};

const mapStateToProps = state => ({
  user: state.user,
  locale: state.common.locale
});

export default connect(mapStateToProps, null)(MyRWDatasetsStarred);
<commit_msg>fix at Profile --> Starred datasets<commit_after>import React from 'react';
import PropTypes from 'prop-types';

// Redux
import { connect } from 'react-redux';

// Services
import UserService from 'services/UserService';
import DatasetService from 'services/DatasetService';

// Components
import Spinner from 'components/ui/Spinner';
import DatasetList from 'components/app/explore/DatasetList';

class MyRWDatasetsStarred extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      user: null,
      starredDatasets: [],
      starredDatasetsLoaded: null
    };

    // User service
    this.userService = new UserService({ apiURL: process.env.CONTROL_TOWER_URL });
  }

  componentDidMount() {
    this.loadDatasets();
  }

  loadDatasets() {
    const { user } = this.props;

    const favourites = user.favourites;
    const datasetIds = favourites.filter(f => f.attributes.resourceType === 'dataset').map(elem => elem.attributes.resourceId);

    if (datasetIds.length) {
      DatasetService.getDatasets(datasetIds, this.props.locale, 'widget,layer,vocabulary,metadata')
        .then((resp) => {
          this.setState({
            starredDatasets: resp,
            starredDatasetsLoaded: true
          });
        })
        .catch((err) => {
          console.error(err);
        });
    } else {
      this.setState({
        starredDatasetsLoaded: true
      });
    }
  }

  render() {
    const { user } = this.props;
    const { starredDatasets, starredDatasetsLoaded } = this.state;
    return (

            <Spinner
              isLoading={!starredDatasetsLoaded}
              className="-relative -light"
            />

            {starredDatasets &&
              <DatasetList
                list={starredDatasets}
                favourites={user.favourites}
                mode="grid"
                showActions={false}
                showFavorite
              />
            }

            {starredDatasetsLoaded && starredDatasets && starredDatasets.length === 0 &&

                You currently have no starred datasets

            }

    );
  }
}

MyRWDatasetsStarred.propTypes = {
  // Store
  user: PropTypes.object.isRequired,
  locale: PropTypes.string.isRequired
};

const mapStateToProps = state => ({
  user: state.user,
  locale: state.common.locale
});

export default connect(mapStateToProps, null)(MyRWDatasetsStarred);
<|endoftext|>


<commit_before>'use strict';
/**
 * Github FileBrowser configuration module.
 */

var m = require('mithril');
var $ = require('jquery');
var URI = require('URIjs');
var Fangorn = require('js/fangorn');
var waterbutler = require('js/waterbutler');

// Cross browser key codes for the Command key
var commandKeys = [224, 17, 91, 93];

function _uploadUrl(item, file) {
    return waterbutler.buildTreeBeardUpload(item, file, {branch: item.data.branch});
}

// TODO: Refactor, repeating from core function too much
function _removeEvent (event, items) {
    var tb = this;
    function cancelDelete() {
        tb.modal.dismiss();
    }

    function runDelete (item) {
        tb.select('.tb-modal-footer .text-danger').html(' Deleting...').css('color', 'grey');;
        // delete from server, if successful delete from view
        $.ajax({
            url: waterbutler.buildTreeBeardDelete(item, {branch: item.data.branch, sha: item.data.extra.fileSha}),
            type : 'DELETE'
        }).done(function (data) {
                // delete view
                tb.deleteNode(item.parentID, item.id);
                Fangorn.Utils.dismissToolbar.call(tb);
                tb.modal.dismiss();
                tb.clearMultiselect();

        }).fail(function (data) {
                tb.modal.dismiss();
                Fangorn.Utils.dismissToolbar.call(tb);
                item.notify.update('Delete failed.', 'danger', undefined, 3000);
                tb.clearMultiselect();
        });
    }

    function runDeleteMultiple(items){
        items.forEach(function(item){
            runDelete(item);
        });
    }

    // If there is only one item being deleted, don't complicate the issue:
    if(items.length === 1) {
        var parent = items[0].parent();
        var mithrilContentSingle = m('div', [
            m('h3.break-word', 'Delete "' + items[0].data.name + '"'),
            m('p', 'This action is irreversible.'),
            parent.children.length < 2 ? m('p', 'If a folder in Github has no children it will automatically be removed.') : ''
        ]);
        var mithrilButtonsSingle = m('div', [
            m('span.tb-modal-btn', { 'class' : 'text-primary', onclick : function() { cancelDelete(); } }, 'Cancel'),
            m('span.tb-modal-btn', { 'class' : 'text-danger', onclick : function() { runDelete(items[0]); }  }, 'Delete')
        ]);
        // This is already being checked before this step but will keep this edit permission check
        if(items[0].data.permissions.edit){
            tb.modal.update(mithrilContentSingle, mithrilButtonsSingle);
        }
    } else {
        // Check if all items can be deleted
        var canDelete = true;
        var deleteList = [];
        var noDeleteList = [];
        var mithrilContentMultiple;
        var mithrilButtonsMultiple;
        items.forEach(function(item, index, arr){
            if(!item.data.permissions.edit){
                canDelete = false;
                noDeleteList.push(item);
            } else {
                deleteList.push(item);
            }
        });
        // If all items can be deleted
        if(canDelete){
            mithrilContentMultiple = m('div', [
                    m('h3.break-word', 'Delete multiple files?'),
                    m('p', 'This action is irreversible.'),
                    deleteList.map(function(item){
                        return m('.fangorn-canDelete.text-success', item.data.name);
                    })
                ]);
            mithrilButtonsMultiple =  m('div', [
                    m('span.tb-modal-btn', { 'class' : 'text-primary', onclick : function() { cancelDelete(); } }, 'Cancel'),
                    m('span.tb-modal-btn', { 'class' : 'text-danger', onclick : function() { runDeleteMultiple.call(tb, deleteList); }  }, 'Delete All')
                ]);
        } else {
            mithrilContentMultiple = m('div', [
                    m('h3.break-word', 'Delete multiple files?'),
                    m('p', 'Some of these files can\'t be deleted but you can delete the ones highlighted with green. This action is irreversible.'),
                    deleteList.map(function(n){
                        return m('.fangorn-canDelete.text-success', n.data.name);
                    }),
                    noDeleteList.map(function(n){
                        return m('.fangorn-noDelete.text-warning', n.data.name);
                    })
                ]);
            mithrilButtonsMultiple =  m('div', [
                    m('span.tb-modal-btn', { 'class' : 'text-primary', onclick : function() { cancelDelete(); } }, 'Cancel'),
                    m('span.tb-modal-btn', { 'class' : 'text-danger', onclick : function() { runDeleteMultiple.call(tb, deleteList); }  }, 'Delete Some')
                ]);
        }
        tb.modal.update(mithrilContentMultiple, mithrilButtonsMultiple);
    }

    return true; // Let fangorn know this config option was used.
}

// Define Fangorn Button Actions
var _githubItemButtons = {
    view: function (ctrl, args, children) {
        var tb = args.treebeard;
        var item = args.item;
        var buttons = [];
        function _downloadEvent(event, item, col) {
            event.stopPropagation();
            window.location = waterbutler.buildTreeBeardDownload(item, {fileSha: item.data.extra.fileSha});
        }
        // Download Zip File
        if (item.kind === 'folder') {
            var branchArray = [];
            if (item.data.branches) {
                item.data.branch = item.data.branch || item.data.defaultBranch;
                for (var i = 0; i < item.data.branches.length; i++) {
                    var selected = item.data.branches[i] === item.data.branch ? 'selected' : '';
                    branchArray.push(m('option', {
                        selected: selected,
                        value: item.data.branches[i]
                    }, item.data.branches[i]));
                }
            }
            // If File and FileRead are not defined dropzone is not supported and neither is uploads
            if (window.File && window.FileReader && item.data.permissions && item.data.permissions.edit) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            Fangorn.ButtonEvents._uploadEvent.call(tb, event, item);
                        },
                        icon: 'fa fa-upload',
                        className: 'text-success'
                    }, 'Upload'),
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            tb.toolbarMode(Fangorn.Components.toolbarModes.ADDFOLDER);
                        },
                        icon: 'fa fa-plus',
                        className: 'text-primary'
                    }, 'Create Folder')
                );
            }
            if (item.data.addonFullname) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            window.location = item.data.urls.zip;
                        },
                        icon: 'fa fa-download',
                        className: 'text-success'
                    }, 'Download'),
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            window.open(item.data.urls.repo, '_blank');
                        },
                        icon: 'fa fa-external-link',
                        className: 'text-info'
                    }, 'Open'),
                    m.component(Fangorn.Components.dropdown, {
                        'label': 'Branch: ',
                        onchange: function (event) {
                            changeBranch.call(tb, item, event.target.value);
                        },
                        icon: 'fa fa-external-link',
                        className: 'text-info'
                    }, branchArray)
                );
            }
        } else if (item.kind === 'file') {
            buttons.push(
                m.component(Fangorn.Components.button, {
                    onclick: function (event) {
                        _downloadEvent.call(tb, event, item);
                    },
                    icon: 'fa fa-download',
                    className: 'text-info'
                }, 'Download')
            );
            if (item.data.permissions && item.data.permissions.edit) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            _removeEvent.call(tb, event, [item]);
                        },
                        icon: 'fa fa-trash',
                        className: 'text-danger'
                    }, 'Delete')
                );
            }
            if (item.data.permissions && item.data.permissions.view) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function(event) {
                            gotoFile.call(tb, item);
                        },
                        icon: 'fa fa-external-link',
                        className : 'text-info'
                    }, 'View'));

            }
        }

        return m('span', buttons); // Tell fangorn this function is used.
    }
};

function changeBranch(item, ref){
    item.data.branch = ref;
    this.updateFolder(null, item);
}

function _resolveLazyLoad(item) {
    return waterbutler.buildTreeBeardMetadata(item, {ref: item.data.branch});
}

function _fangornLazyLoadOnLoad (tree, event) {
    var tb = this;
    tree.children.forEach(function(item) {
        Fangorn.Utils.inheritFromParent(item, tree, ['branch']);
    });
    Fangorn.Utils.setCurrentFileID.call(tb, tree, window.contextVars.node.id, window.contextVars.file);
    if(!event){
        Fangorn.Utils.scrollToFile.call(tb, tb.currentFileID);
    }
}

function gotoFile (item) {
    var tb = this;
    var fileurl = new URI(item.data.nodeUrl)
        .segment('files')
        .segment(item.data.provider)
        .segment(item.data.path.substring(1))
        .search({branch: item.data.branch})
        .toString();
    if(commandKeys.indexOf(tb.pressedKey) !== -1) {
        window.open(fileurl, '_blank');
    } else {
        window.open(fileurl, '_self');
    }
}
function _fangornGithubTitle(item, col)  {
    var tb = this;
    if (item.data.addonFullname) {
        var branch = item.data.branch || item.data.defaultBranch;
        return m('span',[
            m('github-name', item.data.name + ' (' + branch + ')')
        ]);
    } else {
        if (item.kind === 'file' && item.data.permissions.view) {
            return m('span',[
                m('github-name.fg-file-links', {
                    onclick: function() {
                        gotoFile.call(tb, item);
                    }
                }, item.data.name)]);
        } else {
            return m('span', item.data.name);
        }
    }
}

function _fangornColumns (item) {
    var tb = this;
    var selectClass = '';
    var node = item.parent().parent();
    if (item.data.kind === 'file' && tb.currentFileID === item.id) {
        item.css = 'fangorn-selected';
        tb.multiselected([item]);
    }

    var columns = [];

    columns.push({
        data : 'name',
        folderIcons : true,
        filter: true,
        custom : _fangornGithubTitle
    });

    if(tb.options.placement === 'project-files') {
        columns.push(
        {
            data  : 'downloads',
            filter : false,
            css : ''
        });
    }
    return columns;
}

function _fangornFolderIcons(item){
    if(item.data.iconUrl){
        return m('img',{src:item.data.iconUrl, style:{width:'16px', height:'auto'}}, ' ');
    }
    return undefined;
}

function _fangornUploadComplete(item){
    var index = this.returnIndex(item.id);
}

function _fangornUploadSuccess(file, item, response) {
    if (response) {
        response.branch = item.parent().data.branch;
    }
}

// Register configuration
Fangorn.config.github = {
    // Handle changing the branch select
    uploadUrl: _uploadUrl,
    lazyload: _resolveLazyLoad,
    resolveRows: _fangornColumns,
    folderIcon: _fangornFolderIcons,
    onUploadComplete: _fangornUploadComplete,
    lazyLoadOnLoad: _fangornLazyLoadOnLoad,
    uploadSuccess: _fangornUploadSuccess,
    itemButtons: _githubItemButtons,
    removeEvent : _removeEvent
};
<commit_msg>Add the rename button to github<commit_after>'use strict';
/**
 * Github FileBrowser configuration module.
 */

var m = require('mithril');
var $ = require('jquery');
var URI = require('URIjs');
var Fangorn = require('js/fangorn');
var waterbutler = require('js/waterbutler');

// Cross browser key codes for the Command key
var commandKeys = [224, 17, 91, 93];

function _uploadUrl(item, file) {
    return waterbutler.buildTreeBeardUpload(item, file, {branch: item.data.branch});
}

// TODO: Refactor, repeating from core function too much
function _removeEvent (event, items) {
    var tb = this;
    function cancelDelete() {
        tb.modal.dismiss();
    }

    function runDelete (item) {
        tb.select('.tb-modal-footer .text-danger').html(' Deleting...').css('color', 'grey');;
        // delete from server, if successful delete from view
        $.ajax({
            url: waterbutler.buildTreeBeardDelete(item, {branch: item.data.branch, sha: item.data.extra.fileSha}),
            type : 'DELETE'
        }).done(function (data) {
                // delete view
                tb.deleteNode(item.parentID, item.id);
                Fangorn.Utils.dismissToolbar.call(tb);
                tb.modal.dismiss();
                tb.clearMultiselect();

        }).fail(function (data) {
                tb.modal.dismiss();
                Fangorn.Utils.dismissToolbar.call(tb);
                item.notify.update('Delete failed.', 'danger', undefined, 3000);
                tb.clearMultiselect();
        });
    }

    function runDeleteMultiple(items){
        items.forEach(function(item){
            runDelete(item);
        });
    }

    // If there is only one item being deleted, don't complicate the issue:
    if(items.length === 1) {
        var parent = items[0].parent();
        var mithrilContentSingle = m('div', [
            m('h3.break-word', 'Delete "' + items[0].data.name + '"'),
            m('p', 'This action is irreversible.'),
            parent.children.length < 2 ? m('p', 'If a folder in Github has no children it will automatically be removed.') : ''
        ]);
        var mithrilButtonsSingle = m('div', [
            m('span.tb-modal-btn', { 'class' : 'text-primary', onclick : function() { cancelDelete(); } }, 'Cancel'),
            m('span.tb-modal-btn', { 'class' : 'text-danger', onclick : function() { runDelete(items[0]); }  }, 'Delete')
        ]);
        // This is already being checked before this step but will keep this edit permission check
        if(items[0].data.permissions.edit){
            tb.modal.update(mithrilContentSingle, mithrilButtonsSingle);
        }
    } else {
        // Check if all items can be deleted
        var canDelete = true;
        var deleteList = [];
        var noDeleteList = [];
        var mithrilContentMultiple;
        var mithrilButtonsMultiple;
        items.forEach(function(item, index, arr){
            if(!item.data.permissions.edit){
                canDelete = false;
                noDeleteList.push(item);
            } else {
                deleteList.push(item);
            }
        });
        // If all items can be deleted
        if(canDelete){
            mithrilContentMultiple = m('div', [
                    m('h3.break-word', 'Delete multiple files?'),
                    m('p', 'This action is irreversible.'),
                    deleteList.map(function(item){
                        return m('.fangorn-canDelete.text-success', item.data.name);
                    })
                ]);
            mithrilButtonsMultiple =  m('div', [
                    m('span.tb-modal-btn', { 'class' : 'text-primary', onclick : function() { cancelDelete(); } }, 'Cancel'),
                    m('span.tb-modal-btn', { 'class' : 'text-danger', onclick : function() { runDeleteMultiple.call(tb, deleteList); }  }, 'Delete All')
                ]);
        } else {
            mithrilContentMultiple = m('div', [
                    m('h3.break-word', 'Delete multiple files?'),
                    m('p', 'Some of these files can\'t be deleted but you can delete the ones highlighted with green. This action is irreversible.'),
                    deleteList.map(function(n){
                        return m('.fangorn-canDelete.text-success', n.data.name);
                    }),
                    noDeleteList.map(function(n){
                        return m('.fangorn-noDelete.text-warning', n.data.name);
                    })
                ]);
            mithrilButtonsMultiple =  m('div', [
                    m('span.tb-modal-btn', { 'class' : 'text-primary', onclick : function() { cancelDelete(); } }, 'Cancel'),
                    m('span.tb-modal-btn', { 'class' : 'text-danger', onclick : function() { runDeleteMultiple.call(tb, deleteList); }  }, 'Delete Some')
                ]);
        }
        tb.modal.update(mithrilContentMultiple, mithrilButtonsMultiple);
    }

    return true; // Let fangorn know this config option was used.
}

// Define Fangorn Button Actions
var _githubItemButtons = {
    view: function (ctrl, args, children) {
        var tb = args.treebeard;
        var item = args.item;
        var buttons = [];
        function _downloadEvent(event, item, col) {
            event.stopPropagation();
            window.location = waterbutler.buildTreeBeardDownload(item, {fileSha: item.data.extra.fileSha});
        }
        // Download Zip File
        if (item.kind === 'folder') {
            var branchArray = [];
            if (item.data.branches) {
                item.data.branch = item.data.branch || item.data.defaultBranch;
                for (var i = 0; i < item.data.branches.length; i++) {
                    var selected = item.data.branches[i] === item.data.branch ? 'selected' : '';
                    branchArray.push(m('option', {
                        selected: selected,
                        value: item.data.branches[i]
                    }, item.data.branches[i]));
                }
            }
            // If File and FileRead are not defined dropzone is not supported and neither is uploads
            if (window.File && window.FileReader && item.data.permissions && item.data.permissions.edit) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            Fangorn.ButtonEvents._uploadEvent.call(tb, event, item);
                        },
                        icon: 'fa fa-upload',
                        className: 'text-success'
                    }, 'Upload'),
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            tb.toolbarMode(Fangorn.Components.toolbarModes.ADDFOLDER);
                        },
                        icon: 'fa fa-plus',
                        className: 'text-primary'
                    }, 'Create Folder')
                );
            }
            if (item.data.addonFullname) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            window.location = item.data.urls.zip;
                        },
                        icon: 'fa fa-download',
                        className: 'text-success'
                    }, 'Download'),
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            window.open(item.data.urls.repo, '_blank');
                        },
                        icon: 'fa fa-external-link',
                        className: 'text-info'
                    }, 'Open'),
                    m.component(Fangorn.Components.dropdown, {
                        'label': 'Branch: ',
                        onchange: function (event) {
                            changeBranch.call(tb, item, event.target.value);
                        },
                        icon: 'fa fa-external-link',
                        className: 'text-info'
                    }, branchArray)
                );
            }
        } else if (item.kind === 'file') {
            buttons.push(
                m.component(Fangorn.Components.button, {
                    onclick: function (event) {
                        _downloadEvent.call(tb, event, item);
                    },
                    icon: 'fa fa-download',
                    className: 'text-info'
                }, 'Download')
            );
            if (item.data.permissions && item.data.permissions.edit) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function (event) {
                            _removeEvent.call(tb, event, [item]);
                        },
                        icon: 'fa fa-trash',
                        className: 'text-danger'
                    }, 'Delete')
                );
            }
            if (item.data.permissions && item.data.permissions.view) {
                buttons.push(
                    m.component(Fangorn.Components.button, {
                        onclick: function(event) {
                            gotoFile.call(tb, item);
                        },
                        icon: 'fa fa-external-link',
                        className : 'text-info'
                    }, 'View'));

            }
        }

        if(item.data.provider && !item.data.isAddonRoot && item.data.permissions && item.data.permissions.edit) {
            buttons.push(
                m.component(Fangorn.Components.button, {
                    onclick: function() {
                        tb.toolbarMode(Fangorn.Components.toolbarModes.RENAME);
                    },
                    tooltip: 'Change the name of the item',
                    icon: 'fa fa-font',
                    className : 'text-info'
                }, 'Rename')
            );
        }

        return m('span', buttons); // Tell fangorn this function is used.
    }
};

function changeBranch(item, ref){
    item.data.branch = ref;
    this.updateFolder(null, item);
}

function _resolveLazyLoad(item) {
    return waterbutler.buildTreeBeardMetadata(item, {ref: item.data.branch});
}

function _fangornLazyLoadOnLoad (tree, event) {
    var tb = this;
    tree.children.forEach(function(item) {
        Fangorn.Utils.inheritFromParent(item, tree, ['branch']);
    });
    Fangorn.Utils.setCurrentFileID.call(tb, tree, window.contextVars.node.id, window.contextVars.file);
    if(!event){
        Fangorn.Utils.scrollToFile.call(tb, tb.currentFileID);
    }
}

function gotoFile (item) {
    var tb = this;
    var fileurl = new URI(item.data.nodeUrl)
        .segment('files')
        .segment(item.data.provider)
        .segment(item.data.path.substring(1))
        .search({branch: item.data.branch})
        .toString();
    if(commandKeys.indexOf(tb.pressedKey) !== -1) {
        window.open(fileurl, '_blank');
    } else {
        window.open(fileurl, '_self');
    }
}
function _fangornGithubTitle(item, col)  {
    var tb = this;
    if (item.data.addonFullname) {
        var branch = item.data.branch || item.data.defaultBranch;
        return m('span',[
            m('github-name', item.data.name + ' (' + branch + ')')
        ]);
    } else {
        if (item.kind === 'file' && item.data.permissions.view) {
            return m('span',[
                m('github-name.fg-file-links', {
                    onclick: function() {
                        gotoFile.call(tb, item);
                    }
                }, item.data.name)]);
        } else {
            return m('span', item.data.name);
        }
    }
}

function _fangornColumns (item) {
    var tb = this;
    var selectClass = '';
    var node = item.parent().parent();
    if (item.data.kind === 'file' && tb.currentFileID === item.id) {
        item.css = 'fangorn-selected';
        tb.multiselected([item]);
    }

    var columns = [];

    columns.push({
        data : 'name',
        folderIcons : true,
        filter: true,
        custom : _fangornGithubTitle
    });

    if(tb.options.placement === 'project-files') {
        columns.push(
        {
            data  : 'downloads',
            filter : false,
            css : ''
        });
    }
    return columns;
}

function _fangornFolderIcons(item){
    if(item.data.iconUrl){
        return m('img',{src:item.data.iconUrl, style:{width:'16px', height:'auto'}}, ' ');
    }
    return undefined;
}

function _fangornUploadComplete(item){
    var index = this.returnIndex(item.id);
}

function _fangornUploadSuccess(file, item, response) {
    if (response) {
        response.branch = item.parent().data.branch;
    }
}

// Register configuration
Fangorn.config.github = {
    // Handle changing the branch select
    uploadUrl: _uploadUrl,
    lazyload: _resolveLazyLoad,
    resolveRows: _fangornColumns,
    folderIcon: _fangornFolderIcons,
    onUploadComplete: _fangornUploadComplete,
    lazyLoadOnLoad: _fangornLazyLoadOnLoad,
    uploadSuccess: _fangornUploadSuccess,
    itemButtons: _githubItemButtons,
    removeEvent : _removeEvent
};
<|endoftext|>


<commit_before>import React from 'react';
import { Editor } from './Editor';
import { CodegenOutput } from './CodegenOutput';
import classes from './styles.module.css';

export default function LiveDemoEditors({
  setSchema,
  schema,
  setDocuments,
  documents,
  setConfig,
  config,
  mode,
  error,
  output,
}) {
  return (
    <>

          schema.graphql

        <Editor lang={'graphql'} onEdit={setSchema} value={schema} />

          operation.graphql

        <Editor
          lang={'graphql'}
          onEdit={setDocuments}
          value={documents || `# This example isn't\n# using GraphQL operations`}
        />

          codegen.yml

        <Editor lang={'yaml'} onEdit={setConfig} value={config} />

        <CodegenOutput
          editorProps={{
            lang: mode,
            readOnly: true,
          }}
          error={error}
          outputArray={output}
        />

    </>
  );
}
<commit_msg>[deploy_website] fix<commit_after>import React from 'react';
import classes from './styles.module.css';
import { Editor } from './Editor';
import { CodegenOutput } from './CodegenOutput';

export default function LiveDemoEditors({
  setSchema,
  schema,
  setDocuments,
  documents,
  setConfig,
  config,
  mode,
  error,
  output,
}) {
  return (
    <>

          schema.graphql

        <Editor lang={'graphql'} onEdit={setSchema} value={schema} />

          operation.graphql

        <Editor
          lang={'graphql'}
          onEdit={setDocuments}
          value={documents || `# This example isn't\n# using GraphQL operations`}
        />

          codegen.yml

        <Editor lang={'yaml'} onEdit={setConfig} value={config} />

        <CodegenOutput
          editorProps={{
            lang: mode,
            readOnly: true,
          }}
          error={error}
          outputArray={output}
        />

    </>
  );
}
<|endoftext|>


<commit_before>// import Flatpickr
import Flatpickr from "stimulus-flatpickr";
import { ar } from "flatpickr/dist/l10n/ar";
import { cat } from "flatpickr/dist/l10n/cat";
import { cy } from "flatpickr/dist/l10n/cy";
import { de } from "flatpickr/dist/l10n/de";
import { fr } from "flatpickr/dist/l10n/fr";
import { it } from "flatpickr/dist/l10n/it";
import { nl } from "flatpickr/dist/l10n/nl";
import { pl } from "flatpickr/dist/l10n/pl";
import { pt } from "flatpickr/dist/l10n/pt";
import { ru } from "flatpickr/dist/l10n/ru";
import { sv } from "flatpickr/dist/l10n/sv";
import { tr } from "flatpickr/dist/l10n/tr";
import { en } from "flatpickr/dist/l10n/default.js";
import ShortcutButtonsPlugin from "shortcut-buttons-flatpickr";
import labelPlugin from "flatpickr/dist/plugins/labelPlugin/labelPlugin";

export default class extends Flatpickr {
  static values = { enableTime: Boolean, mode: String };
  static targets = ["start", "end"];
  locales = {
    ar: ar,
    cat: cat,
    cy: cy,
    de: de,
    fr: fr,
    it: it,
    nl: nl,
    pl: pl,
    pt: pt,
    ru: ru,
    sv: sv,
    tr: tr,
    en: en,
  };

  initialize() {
    const datetimepicker = this.enableTimeValue == true;
    const mode = this.modeValue == "range" ? "range" : "single";
    // sets your language (you can also set some global setting for all time pickers)
    this.config = {
      altInput: true,
      altFormat: datetimepicker
        ? Spree.translations.flatpickr_datetime_format
        : Spree.translations.flatpickr_date_format,
      dateFormat: datetimepicker ? "Y-m-d H:i" : "Y-m-d",
      enableTime: datetimepicker,
      time_24hr: datetimepicker,
      locale: I18n.base_locale,
      plugins: this.plugins(mode, datetimepicker),
      mode,
    };
  }

  clear(e) {
    this.fp.setDate(null);
  }

  change(selectedDates, dateStr, instance) {
    if (this.hasStartTarget && this.hasEndTarget) {
      this.startTarget.value = selectedDates[0]
        ? this.fp.formatDate(selectedDates[0], this.config.dateFormat)
        : "";
      this.endTarget.value = selectedDates[1]
        ? this.fp.formatDate(selectedDates[1], this.config.dateFormat)
        : "";
      // Also, send event to be sure that ng-model is well updated
      this.startTarget.dispatchEvent(new Event("change"));
      this.endTarget.dispatchEvent(new Event("change"));
    }
  }

  // private

  plugins = (mode, datetimepicker) => {
    const buttons = [{ label: Spree.translations.close }];
    if (mode == "single") {
      buttons.unshift({
        label: datetimepicker
          ? Spree.translations.now
          : Spree.translations.today,
      });
    }
    return [
      ShortcutButtonsPlugin({
        button: buttons,
        label: mode != "range" && "or",
        onClick: this.onClickButtons,
      }),
      labelPlugin({}),
    ];
  };

  onClickButtons = (index, fp) => {
    // Memorize index used for the 'Close' and 'Today|Now' buttons
    // it has index of 1 in case of single mode (ie. can set Today or Now date)
    // it has index of 0 in case of range mode (no Today or Now button)
    const closeButtonIndex = this.modeValue == "range" ? 0 : 1;
    const todayButtonIndex = this.modeValue == "range" ? null : 0;
    switch (index) {
      case todayButtonIndex:
        fp.setDate(new Date(), true);
        break;
      case closeButtonIndex:
        fp.close();
        break;
    }
  };
}
<commit_msg>Remove the `or` label as it was unecessary<commit_after>// import Flatpickr
import Flatpickr from "stimulus-flatpickr";
import { ar } from "flatpickr/dist/l10n/ar";
import { cat } from "flatpickr/dist/l10n/cat";
import { cy } from "flatpickr/dist/l10n/cy";
import { de } from "flatpickr/dist/l10n/de";
import { fr } from "flatpickr/dist/l10n/fr";
import { it } from "flatpickr/dist/l10n/it";
import { nl } from "flatpickr/dist/l10n/nl";
import { pl } from "flatpickr/dist/l10n/pl";
import { pt } from "flatpickr/dist/l10n/pt";
import { ru } from "flatpickr/dist/l10n/ru";
import { sv } from "flatpickr/dist/l10n/sv";
import { tr } from "flatpickr/dist/l10n/tr";
import { en } from "flatpickr/dist/l10n/default.js";
import ShortcutButtonsPlugin from "shortcut-buttons-flatpickr";
import labelPlugin from "flatpickr/dist/plugins/labelPlugin/labelPlugin";

export default class extends Flatpickr {
  static values = { enableTime: Boolean, mode: String };
  static targets = ["start", "end"];
  locales = {
    ar: ar,
    cat: cat,
    cy: cy,
    de: de,
    fr: fr,
    it: it,
    nl: nl,
    pl: pl,
    pt: pt,
    ru: ru,
    sv: sv,
    tr: tr,
    en: en,
  };

  initialize() {
    const datetimepicker = this.enableTimeValue == true;
    const mode = this.modeValue == "range" ? "range" : "single";
    // sets your language (you can also set some global setting for all time pickers)
    this.config = {
      altInput: true,
      altFormat: datetimepicker
        ? Spree.translations.flatpickr_datetime_format
        : Spree.translations.flatpickr_date_format,
      dateFormat: datetimepicker ? "Y-m-d H:i" : "Y-m-d",
      enableTime: datetimepicker,
      time_24hr: datetimepicker,
      locale: I18n.base_locale,
      plugins: this.plugins(mode, datetimepicker),
      mode,
    };
  }

  clear(e) {
    this.fp.setDate(null);
  }

  change(selectedDates, dateStr, instance) {
    if (this.hasStartTarget && this.hasEndTarget) {
      this.startTarget.value = selectedDates[0]
        ? this.fp.formatDate(selectedDates[0], this.config.dateFormat)
        : "";
      this.endTarget.value = selectedDates[1]
        ? this.fp.formatDate(selectedDates[1], this.config.dateFormat)
        : "";
      // Also, send event to be sure that ng-model is well updated
      this.startTarget.dispatchEvent(new Event("change"));
      this.endTarget.dispatchEvent(new Event("change"));
    }
  }

  // private

  plugins = (mode, datetimepicker) => {
    const buttons = [{ label: Spree.translations.close }];
    if (mode == "single") {
      buttons.unshift({
        label: datetimepicker
          ? Spree.translations.now
          : Spree.translations.today,
      });
    }
    return [
      ShortcutButtonsPlugin({
        button: buttons,
        onClick: this.onClickButtons,
      }),
      labelPlugin({}),
    ];
  };

  onClickButtons = (index, fp) => {
    // Memorize index used for the 'Close' and 'Today|Now' buttons
    // it has index of 1 in case of single mode (ie. can set Today or Now date)
    // it has index of 0 in case of range mode (no Today or Now button)
    const closeButtonIndex = this.modeValue == "range" ? 0 : 1;
    const todayButtonIndex = this.modeValue == "range" ? null : 0;
    switch (index) {
      case todayButtonIndex:
        fp.setDate(new Date(), true);
        break;
      case closeButtonIndex:
        fp.close();
        break;
    }
  };
}
<|endoftext|>


<commit_before>/* global Vulcan */
import { Posts } from '../../lib/collections/posts'
import Users from 'meteor/vulcan:users';
import { Utils } from 'meteor/vulcan:core';
import htmlparser2 from 'htmlparser2';
import { HTTP } from 'meteor/http';
import url from 'url';

const whitelistedImageHosts = [
  "lesswrong.com",
  "www.lesswrong.com",
  "res.cloudinary.com"
];

// Parse an HTML string and return an array of URLs of images it refers to in
//  tags.
function getImagesInHtml(html)
{
  let images = [];

  let parser = new htmlparser2.Parser({
    onopentag: function(name, attribs) {
      if(name.toLowerCase() === 'img' && attribs.src) {
        images.push(attribs.src);
      }
    }
  });
  parser.write(html);
  parser.end();

  return images;
}

// Parse an HTML string and return an array of URLs that it links to in
// tags.
function getLinksInHtml(html)
{
  let links = [];

  let parser = new htmlparser2.Parser({
    onopentag: function(name, attribs) {
      if(name.toLowerCase() === 'a' && attribs.href) {
        links.push(attribs.href);
      }
    }
  });
  parser.write(html);
  parser.end();

  return links;
}

async function urlIsBroken(url)
{
  try {
    let result = HTTP.call('GET', url, {timeout: 5000});
    if (result.statusCode !== 200) {
      return true;
    } else {
      return false;
    }
  } catch(e) {
    return true;
  }
}

function imageIsOffsite(imageUrl)
{
  const hostname = new url.URL(imageUrl).hostname;

  for(let i=0; i<whitelistedImageHosts.length; i++) {
    if(hostname === whitelistedImageHosts[i])
      return false;
  }

  return true;
}

const describePost = async (post) =>
{
  const author = await Users.findOne({_id: post.userId});
  const postLink = Utils.getSiteUrl() + "posts/"+post._id;
  return `${post.title} by ${author.displayName} [${post.baseScore}]\n    ${postLink}`;
}

// Check a post for broken images, broken links, and offsite images, and return
// a human-readable string describing the outcome. If everything is good
// (nothing broken), returns the empty string; otherwise the result (which is
// meant to be handled by a person) includes the title/author/karma of the
// post and a list of broken things within it.
const checkPost = async (post) => {
  const images = getImagesInHtml(post.htmlBody);
  const links = getLinksInHtml(post.htmlBody);

  let brokenImages = [];
  let offsiteImages = [];
  let brokenLinks = [];

  for(let i=0; i<images.length; i++) {
    let imageUrl = images[i];
    if(await urlIsBroken(imageUrl))
      brokenImages.push(imageUrl);
    else if(imageIsOffsite(imageUrl))
      offsiteImages.push(imageUrl);
  }
  for(let i=0; i<links.length; i++) {
    let linkUrl = links[i];
    if(await urlIsBroken(linkUrl))
      brokenLinks.push(linkUrl);
  }

  let sb = [];
  if(brokenImages.length>0 || offsiteImages.length>0 || brokenLinks.length>0)
  {
    sb.push(await describePost(post)+"\n");
    for(let i=0; i<brokenImages.length; i++)
      sb.push(`    Broken image: ${brokenImages[i]}\n`);
    for(let i=0; i<brokenLinks.length; i++)
      sb.push(`    Broken link: ${brokenLinks[i]}\n`);
    for(let i=0; i<offsiteImages.length; i++)
      sb.push(`    Offsite image: ${offsiteImages[i]}\n`);
  }
  return sb.join("");
};

Vulcan.findBrokenLinks = async () => {
  //eslint-disable-next-line no-console
  console.log("Checking all posts for broken links and images.");

  const postsToCheck = await Posts.find().fetch();

  //eslint-disable-next-line no-console
  console.log("Checking "+postsToCheck.length+" post for broken links and images.");
  for(let i=0; i<postsToCheck.length; i++)
  {
    let post = postsToCheck[i];
    let result = await checkPost(post);
    //eslint-disable-next-line no-console
    console.log(result);
  }
  //eslint-disable-next-line no-console
  console.log("Checked "+postsToCheck.length+" post for broken links and images.");
}<commit_msg>Second iteration of brokenLinksReport<commit_after>/* global Vulcan */
import { Posts } from '../../lib/collections/posts'
import Users from 'meteor/vulcan:users';
import { Utils } from 'meteor/vulcan:core';
import htmlparser2 from 'htmlparser2';
import { HTTP } from 'meteor/http';
import { URL } from 'url';
import fs from 'fs';

const whitelistedImageHosts = [
  "lesswrong.com",
  "www.lesswrong.com",
  "res.cloudinary.com"
];
const baseUrl = "http://www.lesswrong.com";

// Parse an HTML string and return an array of URLs of images it refers to in
//  tags.
function getImagesInHtml(html)
{
  let images = [];

  let parser = new htmlparser2.Parser({
    onopentag: function(name, attribs) {
      if(name.toLowerCase() === 'img' && attribs.src) {
        images.push(attribs.src);
      }
    }
  });
  parser.write(html);
  parser.end();

  return images;
}

// Parse an HTML string and return an array of URLs that it links to in
// tags.
function getLinksInHtml(html)
{
  let links = [];

  let parser = new htmlparser2.Parser({
    onopentag: function(name, attribs) {
      if(name.toLowerCase() === 'a' && attribs.href) {
        links.push(attribs.href);
      }
    }
  });
  parser.write(html);
  parser.end();

  return links;
}

async function urlIsBroken(url)
{
  try {
    let absoluteUrl = new URL(url, baseUrl).toString();
    let result = HTTP.call('GET', absoluteUrl, {timeout: 5000});
    if (result.statusCode >= 300 && result.statusCode <= 399) {
      // Redirect. In principle this shouldn't happen because meteor's HTTP.call
      // is documented to follow redirects by default. But maybe it does happen.
      console.log("Got "+result.statusCode+" redirect on "+absoluteUrl);
      return false;
    } else if (result.statusCode !== 200) {
      return true;
    } else {
      return false;
    }
  } catch(e) {
    return true;
  }
}

function imageIsOffsite(imageUrl)
{
  const hostname = new URL(imageUrl, baseUrl).hostname;

  for(let i=0; i<whitelistedImageHosts.length; i++) {
    if(hostname === whitelistedImageHosts[i])
      return false;
  }

  return true;
}

const describePost = async (post) =>
{
  const author = await Users.findOne({_id: post.userId});
  const postLink = baseUrl + "/posts/"+post._id;
  return `${post.title} by ${author.displayName} [${post.baseScore}]\n    ${postLink}`;
}

// Check a post for broken images, broken links, and offsite images, and return
// a human-readable string describing the outcome. If everything is good
// (nothing broken), returns the empty string; otherwise the result (which is
// meant to be handled by a person) includes the title/author/karma of the
// post and a list of broken things within it.
const checkPost = async (post) => {
  const images = getImagesInHtml(post.htmlBody);
  const links = getLinksInHtml(post.htmlBody);

  let brokenImages = [];
  let offsiteImages = [];
  let brokenLinks = [];

  for(let i=0; i<images.length; i++) {
    let imageUrl = images[i];
    if(await urlIsBroken(imageUrl))
      brokenImages.push(imageUrl);
    else if(imageIsOffsite(imageUrl))
      offsiteImages.push(imageUrl);
  }
  for(let i=0; i<links.length; i++) {
    let linkUrl = links[i];
    if(await urlIsBroken(linkUrl))
      brokenLinks.push(linkUrl);
  }

  if(brokenImages.length>0 || offsiteImages.length>0 || brokenLinks.length>0)
  {
    let sb = [];
    sb.push(await describePost(post)+"\n");
    for(let i=0; i<brokenImages.length; i++)
      sb.push(`    Broken image: ${brokenImages[i]}\n`);
    for(let i=0; i<brokenLinks.length; i++)
      sb.push(`    Broken link: ${brokenLinks[i]}\n`);
    for(let i=0; i<offsiteImages.length; i++)
      sb.push(`    Offsite image: ${offsiteImages[i]}\n`);
    return sb.join("");
  }
  else
  {
    return null;
  }
};

Vulcan.findBrokenLinks = async (
  startDate, endDate,
  output
) => {
  // TODO: Subdivide date range so we don't try to load all posts at once
  // TODO: Retry "broken" links to remove false positives from the list
  let write = null;
  let onFinish = null;

  if (!output) {
    //eslint-disable-next-line no-console
    write = console.log;
  } else if(_.isString(output)) {
    let outputFile = fs.openSync(output, "a");
    write = (str) => fs.writeSync(outputFile, str);
    onFinish = () => fs.closeSync(outputFile);
  } else {
    write = output;
  }

  write("Checking posts for broken links and images.\n");
  let filter = {};
  if(startDate || endDate) {
    filter = {postedAt: {
      $gte: startDate,
      $lte: endDate
    }};
  }
  const postsToCheck = await Posts.find(filter).fetch();

  write("Checking "+postsToCheck.length+" post for broken links and images.\n");
  for(let i=0; i<postsToCheck.length; i++)
  {
    let post = postsToCheck[i];
    let result = await checkPost(post);
    if (result) {
      write(result);
    }
  }
  write("Checked "+postsToCheck.length+" post for broken links and images.\n");

  if(onFinish) onFinish();
}
<|endoftext|>


<commit_before>import React from "react";
import PropTypes from "prop-types";
import Dropzone from 'react-dropzone';
import cx from "classnames";

class FileUpload extends React.PureComponent {
  constructor(props) {
    super(props);
  }

  onDropHandler = (files) => {
    if(typeof this.props.onChange !== 'undefined') {
      this.props.onChange(files);
    }
  }

  render() {
    const {
      className,
      text,
      accept,
      style
    } = this.props;

    return (
      <Dropzone
        style={style}
        className={cx(className)}
        accept={accept}
        onDrop={this.onDropHandler}>
        {text}
      </Dropzone>
    );
  }
}

FileUpload.propTypes = {
  /** An Object, array, or string of CSS classes to apply to form.*/
  "className": PropTypes.node,

  /* The text to be displayed on the fileupload component. */
  "text": PropTypes.string,

  "onChange": PropTypes.func,

  "accept": PropTypes.string,

  "style": PropTypes.string

}

export default FileUpload;
<commit_msg>use correct prop type for className<commit_after>import React from "react";
import PropTypes from "prop-types";
import Dropzone from 'react-dropzone';
import cx from "classnames";

class FileUpload extends React.PureComponent {
  constructor(props) {
    super(props);
  }

  onDropHandler = (files) => {
    if(typeof this.props.onChange !== 'undefined') {
      this.props.onChange(files);
    }
  }

  render() {
    const {
      className,
      text,
      accept,
      style
    } = this.props;

    return (
      <Dropzone
        style={style}
        className={cx(className)}
        accept={accept}
        onDrop={this.onDropHandler}>
        {text}
      </Dropzone>
    );
  }
}

FileUpload.propTypes = {
  /** An Object, array, or string of CSS classes to apply to fileupload.*/
  "className": PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object,
    PropTypes.array
  ]),

  /* The text to be displayed on the fileupload component. */
  "text": PropTypes.string,

  "onChange": PropTypes.func,

  "accept": PropTypes.string,

  "style": PropTypes.string

}

export default FileUpload;
<|endoftext|>