<commit_before>require('../../setup');

describe('artsy alexa', function() {
    it('speaks the year an artist was born', function(done) {
        chai.request(server)
            .post('/alexa/artsy')
            .send(require('./ArtistAgeIntentRequest.json'))
            .end(function(err, res) {
                expect(res.status).to.equal(200);
                var data = JSON.parse(res.text);
                expect(data.response.outputSpeech.type).to.equal('SSML')
                expect(data.response.outputSpeech.ssml).to.equal('<speak>American artist Andy Warhol was born in Pittsburgh in 1928</speak>')
                done();
            });
    });
    it('properly joins dates and places', function(done) {
        var artistAgeIntentRequest = require('./ArtistAgeIntentRequest.json');
        artistAgeIntentRequest.request.intent.slots.ARTIST.value = 'Norman Rockwell'
        chai.request(server)
            .post('/alexa/artsy')
            .send(artistAgeIntentRequest)
            .end(function(err, res) {
                expect(res.status).to.equal(200);
                var data = JSON.parse(res.text);
                expect(data.response.outputSpeech.type).to.equal('SSML')
                expect(data.response.outputSpeech.ssml).to.equal('<speak>American artist Norman Rockwell was born in New York in 1894</speak>')
                done();
            });
    });
    it('only finds the first artist', function(done) {
        var artistAgeIntentRequest = require('./ArtistAgeIntentRequest.json');
        artistAgeIntentRequest.request.intent.slots.ARTIST.value = 'Artist'
        chai.request(server)
            .post('/alexa/artsy')
            .send(artistAgeIntentRequest)
            .end(function(err, res) {
                expect(res.status).to.equal(200);
                var data = JSON.parse(res.text);
                expect(data.response.outputSpeech.type).to.equal('SSML')
                expect(data.response.outputSpeech.ssml).to.equal("<speak>Sorry, I couldn't find an artist named Artist.</speak>")
                done();
            });
    });
    it('supports artists without dates', function(done) {
        var artistAgeIntentRequest = require('./ArtistAgeIntentRequest.json');
        artistAgeIntentRequest.request.intent.slots.ARTIST.value = 'John Doe'
        chai.request(server)
            .post('/alexa/artsy')
            .send(artistAgeIntentRequest)
            .end(function(err, res) {
                expect(res.status).to.equal(200);
                var data = JSON.parse(res.text);
                expect(data.response.outputSpeech.type).to.equal('SSML')
                expect(data.response.outputSpeech.ssml).to.equal("<speak>Sorry, I don't know when or where artist John Doe was born.</speak>")
                done();
            });
    });
});
<commit_msg>Refactored reusable test code.<commit_after>require('../../setup');

describe('artsy alexa', function() {
    artistAgeIntentRequest = function(artistName, cb) {
        var artistAgeIntentRequest = require('./ArtistAgeIntentRequest.json');
        artistAgeIntentRequest.request.intent.slots.ARTIST.value = artistName;
        chai.request(server)
            .post('/alexa/artsy')
            .send(artistAgeIntentRequest)
            .end(function(err, res) {
                expect(res.status).to.equal(200);
                var data = JSON.parse(res.text);
                expect(data.response.outputSpeech.type).to.equal('SSML')
                cb(data.response.outputSpeech.ssml);
            });
    }

    it('speaks the year an artist was born', function(done) {
        artistAgeIntentRequest('Andy Warhol', function(ssml) {
            expect(ssml).to.equal('<speak>American artist Andy Warhol was born in Pittsburgh in 1928</speak>');
            done();
        });
    });

    it('properly joins dates and places', function(done) {
        artistAgeIntentRequest('Norman Rockwell', function(ssml) {
            expect(ssml).to.equal('<speak>American artist Norman Rockwell was born in New York in 1894</speak>');
            done();
        });
    });

    it('only finds the first artist', function(done) {
        artistAgeIntentRequest('Artist', function(ssml) {
            expect(ssml).to.equal("<speak>Sorry, I couldn't find an artist named Artist.</speak>");
            done();
        });

    });

    it('supports artists without dates', function(done) {
        artistAgeIntentRequest('John Doe', function(ssml) {
            expect(ssml).to.equal("<speak>Sorry, I don't know when or where artist John Doe was born.</speak>");
            done();
        });
    });
});
<|endoftext|>


<commit_before>/*
	Extend the Firebase class.
*/
function OfflineFirebase(url) {
	goog.base(this, url);
}
goog.inherits(OfflineFirebase, Firebase);

/*
	Prefix for OfflineFirebase associated localStorage.

	This is used when storing to, retrieving from, and clearing the cache.
*/
OfflineFirebase.namespace = 'ofb_';

/*
	Override the Firebase .on(...) method to allow us to hijack and cache
	data as it comes in.

	Note the cacheOffline flag that has been added to this method. In order
	to enable the offline caching, set this to true.
*/
OfflineFirebase.prototype.on = function(eventType, callback, cancelCallback, context, cacheOffline) {
	var metaCallback = callback;

	if(cacheOffline) {
		metaCallback = function(snapshot) {
			// Store value locally so we can restore it later.
			OfflineFirebase.store(snapshot);

			callback(snapshot);
		};
	}

	// Register the callback with the superclass.
	OfflineFirebase.superClass_.on.call(this, eventType, metaCallback, cancelCallback, context);
};

/*
	Also override the .once(...) method in a similar manner
*/
OfflineFirebase.prototype.once = function(eventType, callback, cancelCallback, context, cacheOffline) {
	var metaCallback = callback;

	if(cacheOffline) {
		metaCallback = function(snapshot) {
			// Store value locally so we can restore it later.
			OfflineFirebase.store(snapshot);

			callback(snapshot);
		};
	}

	// Register the callback with the superclass.
	OfflineFirebase.superClass_.once.call(this, eventType, metaCallback, cancelCallback, context);
};

/*
	Override the child method to return a subclassed OfflineFirebase instead of an instance of
	the superclass.
*/
OfflineFirebase.prototype.child = function(pathString) {
	return new OfflineFirebase(this.toString() + '/' + pathString);
};

/*
	Stores the data from a snapshot in localStorage so we can restore it
	later.

	This works by retrieving the .exportVal() from the snapshot, iterating
	over the object tree, and storing each leaf (a leaf is defined as a node
	named ".value" or ".priority") into a localStorage item.
*/
OfflineFirebase.store = function(snapshot) {
	var initialPath = snapshot.ref().toString();
	var exportVal = snapshot.exportVal();

	OfflineFirebase._walk(initialPath, exportVal, function(path, data) {
		localStorage.setItem(OfflineFirebase.namespace + 'partial_' + path, JSON.stringify(data));	// Update individual paths
	});

	// Log this global path as saved since we need to set it at its topmost node so it can be retrieved.
	// We will recompose it from its parts when saving so that we're sure we have the freshest data.
	// Also note, that if we have already stored one of its parents, we need not set this but we do anyway.
	/*
		Note: if you set /a/b and /a/c, retrieving /a will not be cached since Firebase doesn't know that
		/a/b and /a/c make up the complete contents of /a.
	*/
	localStorage.setItem(OfflineFirebase.namespace + 'full_' + initialPath, 1);
};

/*
	Recursively traverses a primitive JavaScript object that represents the
	exportVal() of a Firebase DataSnapshot and runs callback for each leaf node.
*/
OfflineFirebase._walk = function(path, exportVal, callback) {
	if(!exportVal) return;

	if(typeof exportVal == 'number' || typeof exportVal == 'string') {
		// Leaf node: simple type with null priority
		callback(path, exportVal);
	} else if(exportVal['.value'] && exportVal['.priority']) {
		// Leaf node: simple type with defined priority
		callback(path, {
			'.priority': exportVal['.priority'],
			'.value': exportVal['.value']
		});
	} else {
		if(exportVal['.priority']) {
			// "Leaf" node: complex type with defined priority
			/*
				Note: this is a "leaf" because it needs a node in localStorage
				to store its priority but it also has children that we need
				to iterate over. This is probably an abuse of the "leaf"
				nomenclature.
			*/
			callback(path, {
				'.priority': exportVal['.priority']
			});
		}

		// Recurse over children of this node
		for(var child in exportVal) {
			if(child == '.priority' || child == '.value') continue; // We have already accounted for this pseudo-child earlier

			var childPath = path + '/' + child;
			OfflineFirebase._walk(childPath, exportVal[child], callback);
		}
	}
};

/*
	Takes all objects stored locally and performs a Firebase set operation
	to initialize the Firebase cache on a cold bootup.

	Note: this will overwrite the server value so you should use .validate
	rules to ensure that the proper data is used in the event of a merge
	conflict.

	Eg, in the case of high-scores, your .validate should enforce that the
	new value is higher than the old value. Or store a timestamp that takes
	the newest value of the 2.

	Currently O(n^2) on number of items in localStorage but I suspect we can
	do better... hasn't become a bottleneck for me yet, though.
*/
OfflineFirebase.restore = function() {
	for(var key in localStorage) {
		if(key && key.indexOf(OfflineFirebase.namespace + 'full_') === 0) {
			// This is one of our cached values

			var url = key.substring( (OfflineFirebase.namespace + 'full_' ).length );	// Remove the namespace to retrieve the URL
			var val = OfflineFirebase._reconstitute(url);

			console.log('Setting ' + url + ' to', val);

			var ref = new OfflineFirebase(url);
			ref.on('value', function() {}); // Register a callback so Firebase will cache this location
			ref.set(val); // Populate it with a value
		}
	}
}

/*
	Reconstitute a value from the sum of its parts (which are residing in localStorage).
*/
OfflineFirebase._reconstitute = function(path) {
	var ret = {};

	for(var key in localStorage) {
		var indexOfPath = key.indexOf(OfflineFirebase.namespace + 'partial_' + path);
		if(indexOfPath === 0) {
			// This is a component of the path so we need to add it to ret.

			var val = JSON.parse(localStorage[key]);
			var subKey = key.substring( (OfflineFirebase.namespace + 'partial_' + path).length ); // The path to this node relative to our root.

			// Create the nested object for this fragment if it doesn't already exist
			var currentTarget = ret;
			if(subKey) {
				var parts = subKey.split('/');

				for(var i=0; i<parts.length; i++) {
					if(!parts[i]) continue;
					if(typeof currentTarget[parts[i]] == 'undefined') currentTarget[parts[i]] = {};
					currentTarget = currentTarget[parts[i]];
				}
			}

			// Set the priority and value for our nested object
			if(typeof val == 'object') {
				if(val['.priority']) {
					currentTarget['.priority'] = val['.priority'];
				} else {
					currentTarget['.priority'] = null;
				}

				if(val['.value']) {
					// If this also contains a value primitive, escalate that.
					val = val['.value'];
				} else {
					// Otherwise we don't need to set any data for this node, just its priority.
					continue;
				}
			}

			currentTarget['.value'] = val;
		}
	}

	return ret;
};

/*
	Clear the localStorage of all OfflineFirebase related items.

	Note: these items will still be stored by Firebase's internal cache if
	they have been requested (or stored) already on this page-load.
*/
OfflineFirebase.clear = function() {
	for(var key in localStorage) {
		if(key && key.indexOf(OfflineFirebase.namespace) === 0) {
			// This is one of our cached values so remove it.
			localStorage.removeItem(key);
		}
	}
};
<commit_msg>Removing extraneous logs<commit_after>/*
	Extend the Firebase class.
*/
function OfflineFirebase(url) {
	goog.base(this, url);
}
goog.inherits(OfflineFirebase, Firebase);

/*
	Prefix for OfflineFirebase associated localStorage.

	This is used when storing to, retrieving from, and clearing the cache.
*/
OfflineFirebase.namespace = 'ofb_';

/*
	Override the Firebase .on(...) method to allow us to hijack and cache
	data as it comes in.

	Note the cacheOffline flag that has been added to this method. In order
	to enable the offline caching, set this to true.
*/
OfflineFirebase.prototype.on = function(eventType, callback, cancelCallback, context, cacheOffline) {
	var metaCallback = callback;

	if(cacheOffline) {
		metaCallback = function(snapshot) {
			// Store value locally so we can restore it later.
			OfflineFirebase.store(snapshot);

			callback(snapshot);
		};
	}

	// Register the callback with the superclass.
	OfflineFirebase.superClass_.on.call(this, eventType, metaCallback, cancelCallback, context);
};

/*
	Also override the .once(...) method in a similar manner
*/
OfflineFirebase.prototype.once = function(eventType, callback, cancelCallback, context, cacheOffline) {
	var metaCallback = callback;

	if(cacheOffline) {
		metaCallback = function(snapshot) {
			// Store value locally so we can restore it later.
			OfflineFirebase.store(snapshot);

			callback(snapshot);
		};
	}

	// Register the callback with the superclass.
	OfflineFirebase.superClass_.once.call(this, eventType, metaCallback, cancelCallback, context);
};

/*
	Override the child method to return a subclassed OfflineFirebase instead of an instance of
	the superclass.
*/
OfflineFirebase.prototype.child = function(pathString) {
	return new OfflineFirebase(this.toString() + '/' + pathString);
};

/*
	Stores the data from a snapshot in localStorage so we can restore it
	later.

	This works by retrieving the .exportVal() from the snapshot, iterating
	over the object tree, and storing each leaf (a leaf is defined as a node
	named ".value" or ".priority") into a localStorage item.
*/
OfflineFirebase.store = function(snapshot) {
	var initialPath = snapshot.ref().toString();
	var exportVal = snapshot.exportVal();

	OfflineFirebase._walk(initialPath, exportVal, function(path, data) {
		localStorage.setItem(OfflineFirebase.namespace + 'partial_' + path, JSON.stringify(data));	// Update individual paths
	});

	// Log this global path as saved since we need to set it at its topmost node so it can be retrieved.
	// We will recompose it from its parts when saving so that we're sure we have the freshest data.
	// Also note, that if we have already stored one of its parents, we need not set this but we do anyway.
	/*
		Note: if you set /a/b and /a/c, retrieving /a will not be cached since Firebase doesn't know that
		/a/b and /a/c make up the complete contents of /a.
	*/
	localStorage.setItem(OfflineFirebase.namespace + 'full_' + initialPath, 1);
};

/*
	Recursively traverses a primitive JavaScript object that represents the
	exportVal() of a Firebase DataSnapshot and runs callback for each leaf node.
*/
OfflineFirebase._walk = function(path, exportVal, callback) {
	if(!exportVal) return;

	if(typeof exportVal == 'number' || typeof exportVal == 'string') {
		// Leaf node: simple type with null priority
		callback(path, exportVal);
	} else if(exportVal['.value'] && exportVal['.priority']) {
		// Leaf node: simple type with defined priority
		callback(path, {
			'.priority': exportVal['.priority'],
			'.value': exportVal['.value']
		});
	} else {
		if(exportVal['.priority']) {
			// "Leaf" node: complex type with defined priority
			/*
				Note: this is a "leaf" because it needs a node in localStorage
				to store its priority but it also has children that we need
				to iterate over. This is probably an abuse of the "leaf"
				nomenclature.
			*/
			callback(path, {
				'.priority': exportVal['.priority']
			});
		}

		// Recurse over children of this node
		for(var child in exportVal) {
			if(child == '.priority' || child == '.value') continue; // We have already accounted for this pseudo-child earlier

			var childPath = path + '/' + child;
			OfflineFirebase._walk(childPath, exportVal[child], callback);
		}
	}
};

/*
	Takes all objects stored locally and performs a Firebase set operation
	to initialize the Firebase cache on a cold bootup.

	Note: this will overwrite the server value so you should use .validate
	rules to ensure that the proper data is used in the event of a merge
	conflict.

	Eg, in the case of high-scores, your .validate should enforce that the
	new value is higher than the old value. Or store a timestamp that takes
	the newest value of the 2.

	Currently O(n^2) on number of items in localStorage but I suspect we can
	do better... hasn't become a bottleneck for me yet, though.
*/
OfflineFirebase.restore = function() {
	for(var key in localStorage) {
		if(key && key.indexOf(OfflineFirebase.namespace + 'full_') === 0) {
			// This is one of our cached values

			var url = key.substring( (OfflineFirebase.namespace + 'full_' ).length );	// Remove the namespace to retrieve the URL
			var val = OfflineFirebase._reconstitute(url);

			var ref = new OfflineFirebase(url);
			ref.on('value', function() {}); // Register a callback so Firebase will cache this location
			ref.set(val); // Populate it with a value
		}
	}
}

/*
	Reconstitute a value from the sum of its parts (which are residing in localStorage).
*/
OfflineFirebase._reconstitute = function(path) {
	var ret = {};

	for(var key in localStorage) {
		var indexOfPath = key.indexOf(OfflineFirebase.namespace + 'partial_' + path);
		if(indexOfPath === 0) {
			// This is a component of the path so we need to add it to ret.

			var val = JSON.parse(localStorage[key]);
			var subKey = key.substring( (OfflineFirebase.namespace + 'partial_' + path).length ); // The path to this node relative to our root.

			// Create the nested object for this fragment if it doesn't already exist
			var currentTarget = ret;
			if(subKey) {
				var parts = subKey.split('/');

				for(var i=0; i<parts.length; i++) {
					if(!parts[i]) continue;
					if(typeof currentTarget[parts[i]] == 'undefined') currentTarget[parts[i]] = {};
					currentTarget = currentTarget[parts[i]];
				}
			}

			// Set the priority and value for our nested object
			if(typeof val == 'object') {
				if(val['.priority']) {
					currentTarget['.priority'] = val['.priority'];
				} else {
					currentTarget['.priority'] = null;
				}

				if(val['.value']) {
					// If this also contains a value primitive, escalate that.
					val = val['.value'];
				} else {
					// Otherwise we don't need to set any data for this node, just its priority.
					continue;
				}
			}

			currentTarget['.value'] = val;
		}
	}

	return ret;
};

/*
	Clear the localStorage of all OfflineFirebase related items.

	Note: these items will still be stored by Firebase's internal cache if
	they have been requested (or stored) already on this page-load.
*/
OfflineFirebase.clear = function() {
	for(var key in localStorage) {
		if(key && key.indexOf(OfflineFirebase.namespace) === 0) {
			// This is one of our cached values so remove it.
			localStorage.removeItem(key);
		}
	}
};
<|endoftext|>


<commit_before>'use strict';

module.exports = function (grunt) {
    var path = require('path');

    // load npm tasks
    require('load-grunt-tasks')(grunt);

    // Time how long tasks take. Can help when optimizing build times
    require('time-grunt')(grunt);

    /**
     * Gets the index.html file from the code coverage folder.
     *
     * @param {!string} folder The path to the code coverage folder.
     */
    function getCoverageReport (folder) {
        var reports = grunt.file.expand(folder + '*/index.html');

        if (reports && reports.length > 0) {
            return reports[0];
        }

        return '';
    }

    // Project configuration.
    grunt.initConfig({
        jshintFiles: ['Gruntfile.js', <% if (isNpmPackage) { %>'lib/**/*.js', 'test/**/*.js'<% } %>],
        pkg: grunt.file.readJSON('package.json'),<% if (isBowerPackage) { %>
        banner: '/*!\n' +
            ' * <%%= pkg.title || pkg.name %> - v<%%= pkg.version %> - <%%= grunt.template.today("yyyy-mm-dd") %>\n' +
            '<%%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' +
            ' *\n' +
            ' * Copyright (c) <%%= grunt.template.today("yyyy") %> <%%= pkg.author.name %>\n' +
            ' * Licensed <%%= _.pluck(pkg.licenses, "type").join(", ") %>\n' +
            ' */\n\n',<% } %>
        clean: {
            jasmine: ['build/reports/test'],
            lint: ['build/reports/lint'],
            coverage: ['build/reports/coverage'],
            ci: ['build/reports']<% if (isBowerPackage) { %>,
            tmp: ['.tmp']<% } %>
        },<% if (isBowerPackage) { %>
        concat: {
            dist: {
                options: {
                    stripBanners: true
                },
                src: ['<% if (isNpmPackage) { %>lib<% } else { %>src<% } %>/**/*.js'],
                dest: '<%%= pkg.name %>.js'
            },
            removeUseStrict: {
                options: {
                    banner: '<%%= banner %>\'use strict\';\n',
                    process: function(src) {
                        return src.replace(/(^|\n)[ \t]*('use strict'|"use strict");?\s*/g, '$1');
                    }
                },
                src: ['<%%= pkg.name %>.js'],
                dest: '<%%= pkg.name %>.js'
            }
        },
        uglify: {
            options: {
                banner: '<%%= banner %>',
                sourceMap: true
            },
            dist: {
                src: [<% if (props.useAngular) { %>'.tmp/<%%= pkg.name %>.js'<% } else { %>'<%%= pkg.name %>.js'<% } %>],
                dest: '<%%= pkg.name %>.min.js'
            }
        },<% } %><% if (props.useAngular) { %>
        ngmin: {
            dist: {
                files: [
                {
                    src: ['<%%= pkg.name %>.js'],
                    dest: '.tmp/<%%= pkg.name %>.js'
                }]
            }
        },<% } %>
        jshint: {
            options: {
                jshintrc: true
            },
            test: '<%%= jshintFiles %>',
            jslint: {
                options: {
                    reporter: 'jslint',
                    reporterOutput: 'build/reports/lint/jshint.xml'
                },
                files: {
                    src: '<%%= jshintFiles %>'
                }
            },
            checkstyle: {
                options: {
                    reporter: 'checkstyle',
                    reporterOutput: 'build/reports/lint/jshint_checkstyle.xml'
                },
                files: {
                    src: '<%%= jshintFiles %>'
                }
            }
        },
        bgShell: {
            coverage: {
                cmd: 'node node_modules/istanbul/lib/cli.js cover --dir build/reports/coverage node_modules/grunt-jasmine-node/node_modules/jasmine-node/bin/jasmine-node -- test --forceexit'
            },
            cobertura: {
                cmd: 'node node_modules/istanbul/lib/cli.js report --root build/reports/coverage --dir build/reports/coverage cobertura'
            }
        },
        open: {
            coverage: {
                path: function () {
                    return path.join(__dirname, getCoverageReport('build/reports/coverage/'));
                }
            }
        },<% if (props.useKarma) { %>
        karma: {
            unit: {
                configFile: 'test/karma.conf.js'
            },
            ci: {
                configFile: 'test/karma.conf.js',
                colors: false,
                reporters: ['mocha', 'junit'],
                junitReporter: {
                    outputFile: 'build/reports/tests/<%= slugname %>.xml',
                    suite: '<%= slugname %>'
                }
            },
            debug: {
                configFile: 'test/karma.conf.js',
                singleRun: false,
                detectBrowsers: {
                    enabled: false
                }
            },
            coverage: {
                configFile: 'test/karma.coverage.conf.js',
                colors: false
            },
            cobertura: {
                configFile: 'test/karma.coverage.conf.js',
                colors: false,
                coverageReporter: {
                    type: 'cobertura',
                    dir: 'build/reports/coverage'
                }
            }
        },<% } %>
        jasmine_node: {
            options: {
                specNameMatcher: './*.spec', // load only specs containing specNameMatcher
                requirejs: false,
                forceExit: true
            },
            test: ['test/'],
            ci: {
                options: {
                    jUnit: {
                    report: true,
                    savePath: 'build/reports/test/',
                    useDotNotation: true,
                    consolidate: true
                    }
                },
                src: ['test/']
            }
        },
        changelog: {
            options: {
            }
        },
        bump: {
            options: {
                files: ['package.json' <% if (isBowerPackage) { %>, 'bower.json'<% } %>],
                updateConfigs: ['pkg'],
                commitFiles: ['.'],
                commitMessage: 'chore: release v%VERSION%',
                push: false
            }
        }
    });

    // Register tasks.
    grunt.registerTask('git:commitHook', 'Install git commit hook', function () {
        grunt.file.copy('validate-commit-msg.js', '.git/hooks/commit-msg');
        require('fs').chmodSync('.git/hooks/commit-msg', '0755');
        grunt.log.ok('Registered git hook: commit-msg');
    });

    grunt.registerTask('lint', ['jshint:test']);
    grunt.registerTask('test', ['git:commitHook', 'clean:jasmine', 'jshint:test',<% if (props.useKarma) { %> 'karma:unit' <% } else { %> 'jasmine_node:test'<% } %>]);
    grunt.registerTask('cover', ['clean:coverage', 'jshint:test', <% if (props.useKarma) { %> 'karma:coverage' <% } else { %> 'bgShell:coverage'<% } %>, 'open:coverage']);<% if (props.useKarma) { %>
    grunt.registerTask('debug', ['karma:debug']);<% } %>
    grunt.registerTask('ci', ['clean:ci', 'jshint:jslint', 'jshint:checkstyle',<% if (props.useKarma) { %> 'karma:ci', 'karma:coverage', 'karma:cobertura' <% } else { %> 'jasmine_node:ci', 'bgShell:coverage', 'bgShell:cobertura'<% } %>]);
    grunt.registerTask('release', 'Bump version, update changelog and tag version', function (version) {
        grunt.task.run([
            'bump:' + (version || 'patch') + ':bump-only',
            'changelog',
            'bump-commit'
        ]);
    });

    <% if (isBowerPackage && props.useAngular) { %>grunt.registerTask('build', ['clean:tmp', 'concat', 'ngmin', 'uglify']);<% } else if (isBowerPackage) { %>grunt.registerTask('build', ['clean:tmp', 'concat', 'uglify']);<% } %>

    // Default task.
    grunt.registerTask('default', ['test']);
};<commit_msg>fix: add build task to release task when module is a bower module<commit_after>'use strict';

module.exports = function (grunt) {
    var path = require('path');

    // load npm tasks
    require('load-grunt-tasks')(grunt);

    // Time how long tasks take. Can help when optimizing build times
    require('time-grunt')(grunt);

    /**
     * Gets the index.html file from the code coverage folder.
     *
     * @param {!string} folder The path to the code coverage folder.
     */
    function getCoverageReport (folder) {
        var reports = grunt.file.expand(folder + '*/index.html');

        if (reports && reports.length > 0) {
            return reports[0];
        }

        return '';
    }

    // Project configuration.
    grunt.initConfig({
        jshintFiles: ['Gruntfile.js', <% if (isNpmPackage) { %>'lib/**/*.js', 'test/**/*.js'<% } %>],
        pkg: grunt.file.readJSON('package.json'),<% if (isBowerPackage) { %>
        banner: '/*!\n' +
            ' * <%%= pkg.title || pkg.name %> - v<%%= pkg.version %> - <%%= grunt.template.today("yyyy-mm-dd") %>\n' +
            '<%%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' +
            ' *\n' +
            ' * Copyright (c) <%%= grunt.template.today("yyyy") %> <%%= pkg.author.name %>\n' +
            ' * Licensed <%%= _.pluck(pkg.licenses, "type").join(", ") %>\n' +
            ' */\n\n',<% } %>
        clean: {
            jasmine: ['build/reports/test'],
            lint: ['build/reports/lint'],
            coverage: ['build/reports/coverage'],
            ci: ['build/reports']<% if (isBowerPackage) { %>,
            tmp: ['.tmp']<% } %>
        },<% if (isBowerPackage) { %>
        concat: {
            dist: {
                options: {
                    stripBanners: true
                },
                src: ['<% if (isNpmPackage) { %>lib<% } else { %>src<% } %>/**/*.js'],
                dest: '<%%= pkg.name %>.js'
            },
            removeUseStrict: {
                options: {
                    banner: '<%%= banner %>\'use strict\';\n',
                    process: function(src) {
                        return src.replace(/(^|\n)[ \t]*('use strict'|"use strict");?\s*/g, '$1');
                    }
                },
                src: ['<%%= pkg.name %>.js'],
                dest: '<%%= pkg.name %>.js'
            }
        },
        uglify: {
            options: {
                banner: '<%%= banner %>',
                sourceMap: true
            },
            dist: {
                src: [<% if (props.useAngular) { %>'.tmp/<%%= pkg.name %>.js'<% } else { %>'<%%= pkg.name %>.js'<% } %>],
                dest: '<%%= pkg.name %>.min.js'
            }
        },<% } %><% if (props.useAngular) { %>
        ngmin: {
            dist: {
                files: [
                {
                    src: ['<%%= pkg.name %>.js'],
                    dest: '.tmp/<%%= pkg.name %>.js'
                }]
            }
        },<% } %>
        jshint: {
            options: {
                jshintrc: true
            },
            test: '<%%= jshintFiles %>',
            jslint: {
                options: {
                    reporter: 'jslint',
                    reporterOutput: 'build/reports/lint/jshint.xml'
                },
                files: {
                    src: '<%%= jshintFiles %>'
                }
            },
            checkstyle: {
                options: {
                    reporter: 'checkstyle',
                    reporterOutput: 'build/reports/lint/jshint_checkstyle.xml'
                },
                files: {
                    src: '<%%= jshintFiles %>'
                }
            }
        },
        bgShell: {
            coverage: {
                cmd: 'node node_modules/istanbul/lib/cli.js cover --dir build/reports/coverage node_modules/grunt-jasmine-node/node_modules/jasmine-node/bin/jasmine-node -- test --forceexit'
            },
            cobertura: {
                cmd: 'node node_modules/istanbul/lib/cli.js report --root build/reports/coverage --dir build/reports/coverage cobertura'
            }
        },
        open: {
            coverage: {
                path: function () {
                    return path.join(__dirname, getCoverageReport('build/reports/coverage/'));
                }
            }
        },<% if (props.useKarma) { %>
        karma: {
            unit: {
                configFile: 'test/karma.conf.js'
            },
            ci: {
                configFile: 'test/karma.conf.js',
                colors: false,
                reporters: ['mocha', 'junit'],
                junitReporter: {
                    outputFile: 'build/reports/tests/<%= slugname %>.xml',
                    suite: '<%= slugname %>'
                }
            },
            debug: {
                configFile: 'test/karma.conf.js',
                singleRun: false,
                detectBrowsers: {
                    enabled: false
                }
            },
            coverage: {
                configFile: 'test/karma.coverage.conf.js',
                colors: false
            },
            cobertura: {
                configFile: 'test/karma.coverage.conf.js',
                colors: false,
                coverageReporter: {
                    type: 'cobertura',
                    dir: 'build/reports/coverage'
                }
            }
        },<% } %>
        jasmine_node: {
            options: {
                specNameMatcher: './*.spec', // load only specs containing specNameMatcher
                requirejs: false,
                forceExit: true
            },
            test: ['test/'],
            ci: {
                options: {
                    jUnit: {
                    report: true,
                    savePath: 'build/reports/test/',
                    useDotNotation: true,
                    consolidate: true
                    }
                },
                src: ['test/']
            }
        },
        changelog: {
            options: {
            }
        },
        bump: {
            options: {
                files: ['package.json' <% if (isBowerPackage) { %>, 'bower.json'<% } %>],
                updateConfigs: ['pkg'],
                commitFiles: ['.'],
                commitMessage: 'chore: release v%VERSION%',
                push: false
            }
        }
    });

    // Register tasks.
    grunt.registerTask('git:commitHook', 'Install git commit hook', function () {
        grunt.file.copy('validate-commit-msg.js', '.git/hooks/commit-msg');
        require('fs').chmodSync('.git/hooks/commit-msg', '0755');
        grunt.log.ok('Registered git hook: commit-msg');
    });

    grunt.registerTask('lint', ['jshint:test']);
    grunt.registerTask('test', ['git:commitHook', 'clean:jasmine', 'jshint:test',<% if (props.useKarma) { %> 'karma:unit' <% } else { %> 'jasmine_node:test'<% } %>]);
    grunt.registerTask('cover', ['clean:coverage', 'jshint:test', <% if (props.useKarma) { %> 'karma:coverage' <% } else { %> 'bgShell:coverage'<% } %>, 'open:coverage']);<% if (props.useKarma) { %>
    grunt.registerTask('debug', ['karma:debug']);<% } %>
    grunt.registerTask('ci', ['clean:ci', 'jshint:jslint', 'jshint:checkstyle',<% if (props.useKarma) { %> 'karma:ci', 'karma:coverage', 'karma:cobertura' <% } else { %> 'jasmine_node:ci', 'bgShell:coverage', 'bgShell:cobertura'<% } %>]);
    grunt.registerTask('release', 'Bump version, update changelog and tag version', function (version) {
        grunt.task.run([
            'bump:' + (version || 'patch') + ':bump-only',<% if (isBowerPackage) { %>
            'build',<% } %>
            'changelog',
            'bump-commit'
        ]);
    });

    <% if (isBowerPackage && props.useAngular) { %>grunt.registerTask('build', ['clean:tmp', 'concat', 'ngmin', 'uglify']);<% } else if (isBowerPackage) { %>grunt.registerTask('build', ['clean:tmp', 'concat', 'uglify']);<% } %>

    // Default task.
    grunt.registerTask('default', ['test']);
};<|endoftext|>


<commit_before>// Generated on 2013-09-03 using generator-webapp 0.4.1
'use strict';
var LIVERELOAD_PORT = 35729;
var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT});
var mountFolder = function (connect, dir) {
    return connect.static(require('path').resolve(dir));
};

module.exports = function(grunt) {
  // configurable paths
  var yoConf = {
      dev: 'dev',
      dist: 'dist'
  };
      // show elapsed time at the end
  require('time-grunt')(grunt);
  // load all grunt tasks
  require('load-grunt-tasks')(grunt);
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    quickConfig: yoConf,
    connect: {
      options: {
        port: 9000,
        // change this to '0.0.0.0' to access the server from outside
        hostname: 'localhost'
      },
      livereload: {
        options: {
          middleware: function (connect) {
            return [
              lrSnippet,
              mountFolder(connect, '.tmp'),
              mountFolder(connect, quickConfig.dev)
            ];
          }
        }
      },
      dist: {
        options: {
          middleware: function (connect) {
            return [
              mountFolder(connect, quickConfig.dist)
            ];
          }
        }
      }
    },
    open: {
      server: {
        path: 'http://localhost:<%= connect.options.port %>'
      }
    },
    clean: {
        dist: {
            files: [{
                dot: true,
                src: [
                    '.tmp',
                    '<%= quickConfig.dist %>/*',
                    '!<%= quickConfig.dist %>/.git*'
                ]
            }]
        },
        server: '.tmp'
    },
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
      }
    },
    compass: {
      otions: {
        config: 'config.rb'
      },
      dist: {
        options: {
          environment: 'production'
        }
      },
      dev: {
        options: {
          environment: 'development'
        }
      }
    }
  });

  grunt.registerTask('build', [
    'clean',
    'uglify',
    'compass',
    'connect',
    'open'
  ]);

  grunt.registerTask('default', [
    'clean',
    'uglify',
    'compass',
    'connect',
    'open'
  ]);

};
<commit_msg>Var name correction<commit_after>// Generated on 2013-09-03 using generator-webapp 0.4.1
'use strict';
var LIVERELOAD_PORT = 35729;
var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT});
var mountFolder = function (connect, dir) {
    return connect.static(require('path').resolve(dir));
};

module.exports = function(grunt) {
  // configurable paths
  var yoConf = {
      dev: 'dev',
      dist: 'dist'
  };
      // show elapsed time at the end
  require('time-grunt')(grunt);
  // load all grunt tasks
  require('load-grunt-tasks')(grunt);
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    quickConfig: yoConf,
    connect: {
      options: {
        port: 9000,
        // change this to '0.0.0.0' to access the server from outside
        hostname: 'localhost'
      },
      livereload: {
        options: {
          middleware: function (connect) {
            return [
              lrSnippet,
              mountFolder(connect, '.tmp'),
              mountFolder(connect, yoConf.dev)
            ];
          }
        }
      },
      dist: {
        options: {
          middleware: function (connect) {
            return [
              mountFolder(connect, yoConf.dist)
            ];
          }
        }
      }
    },
    open: {
      server: {
        path: 'http://localhost:<%= connect.options.port %>'
      }
    },
    clean: {
        dist: {
            files: [{
                dot: true,
                src: [
                    '.tmp',
                    '<%= quickConfig.dist %>/*',
                    '!<%= quickConfig.dist %>/.git*'
                ]
            }]
        },
        server: '.tmp'
    },
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
      }
    },
    compass: {
      otions: {
        config: 'config.rb'
      },
      dist: {
        options: {
          environment: 'production'
        }
      },
      dev: {
        options: {
          environment: 'development'
        }
      }
    }
  });

  grunt.registerTask('build', [
    'clean',
    'uglify',
    'compass',
    'connect',
    'open'
  ]);

  grunt.registerTask('default', [
    'clean',
    'uglify',
    'compass',
    'connect',
    'open'
  ]);

};
<|endoftext|>


<commit_before>//META{"name":"ownerTag"}*//

/*@cc_on
@if (@_jscript)
    // Offer to self-install for clueless users that try to run this directly.
    var shell = WScript.CreateObject("WScript.Shell");
    var fs = new ActiveXObject("Scripting.FileSystemObject");
    var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins");
    var pathSelf = WScript.ScriptFullName;
    // Put the user at ease by addressing them in the first person
    shell.Popup("It looks like you mistakenly tried to run me directly. (don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
    if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
        shell.Popup("I'm in the correct folder already.\nJust reload Discord with Ctrl+R.", 0, "I'm already installed", 0x40);
    } else if (!fs.FolderExists(pathPlugins)) {
        shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
    } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
        fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
        // Show the user where to put plugins in the future
        shell.Exec("explorer " + pathPlugins);
        shell.Popup("I'm installed!\nJust reload Discord with Ctrl+R.", 0, "Successfully installed", 0x40);
    }
    WScript.Quit();
@else @*/

var ownerTag = function () {};

(function () {
    "use strict";

    // This is super hackish, and will likely break as Discord's internal API changes
    // Anything using this or what it returns should be prepared to catch some exceptions
    const getInternalInstance = e => e[Object.keys(e).find(k => k.startsWith("__reactInternalInstance"))];

    function getOwnerInstance(e, {include, exclude=["Popout", "Tooltip", "Scroller", "BackgroundFlash"]} = {}) {
        if (e === undefined) {
            return undefined;
        }

        // Set up filter; if no include filter is given, match all except those in exclude
        const excluding = include === undefined;
        const filter = excluding ? exclude : include;

        // Get displayName of the React class associated with this element
        // Based on getName(), but only check for an explicit displayName
        function getDisplayName(owner) {
            const type = owner._currentElement.type;
            const constructor = owner._instance && owner._instance.constructor;
            return type.displayName || constructor && constructor.displayName || null;
        }
        // Check class name against filters
        function classFilter(owner) {
            const name = getDisplayName(owner);
            return (name !== null && !!(filter.includes(name) ^ excluding));
        }

        // Walk up the hierarchy until a proper React object is found
        for (let prev, curr=getInternalInstance(e); !_.isNil(curr); prev=curr, curr=curr._hostParent) {
            // Before checking its parent, try to find a React object for prev among renderedChildren
            // This finds React objects which don't have a direct counterpart in the DOM hierarchy
            // e.g. Message, ChannelMember, ...
            if (prev !== undefined && !_.isNil(curr._renderedChildren)) {
                /* jshint loopfunc: true */
                let owner = Object.values(curr._renderedChildren)
                    .find(v => !_.isNil(v._instance) && v.getHostNode() === prev.getHostNode());
                if (!_.isNil(owner) && classFilter(owner)) {
                    return owner._instance;
                }
            }

            if (_.isNil(curr._currentElement)) {
                continue;
            }

            // Get a React object if one corresponds to this DOM element
            // e.g. .user-popout -> UserPopout, ...
            let owner = curr._currentElement._owner;
            if (!_.isNil(owner) && classFilter(owner)) {
                return owner._instance;
            }
        }

        return null;
    }

    function getInternalProps(e) {
        if (e === undefined) {
            return undefined;
        }

        try {
            return getOwnerInstance(e).props;
        } catch (err) {
            return undefined;
        }
    }

    // Get the relevant user ID for an element, or undefined
    function getUserId(e) {
        var props = getInternalProps(e);
        if (props === undefined) {
            return undefined;
        }

        try {
            return props.user.id;
        } catch (err) {
            // Catch TypeError if no user in props
        }

        try {
            return props.message.author.id;
        } catch (err) {
            // Catch TypeError if no message in props
        }

        return undefined;
    }

    // Get the relevant user color for an element, or undefined
    function getUserColor(e) {
        var props = getInternalProps(e);
        if (props === undefined) {
            return undefined;
        }

        try {
            return props.message.colorString;
        } catch (err) {
            // Catch TypeError if no message in props
        }

        // Return colorString or undefined if not present
        return props.colorString;
    }

    // Convert colorString to array of RGB values
    function hexToRgb(hex) {
        var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? [
            parseInt(result[1], 16),
            parseInt(result[2], 16),
            parseInt(result[3], 16)
        ] : null;
    }

    // Check whether colorString is brighter than middle gray
    // See: https://stackoverflow.com/a/3943023/6807290
    function isBright(color){
        var c = hexToRgb(color);
        return !!c && (c[0]*0.299 + c[1]*0.587 + c[2]*0.114) > 186;
    }

    // Get the relevant guild ID for an element, or undefined
    function getGuildId(e) {
        var owner = getOwnerInstance(e);
        if (owner === undefined) {
            return undefined;
        }

        try {
            return owner.props.guild.id;
        } catch (err) {
            // Catch TypeError if no guild in props
        }

        try {
            return owner.state.guild.id;
        } catch (err) {
            // Catch TypeError if no guild in state
        }

        return undefined;
    }

    // Get the relevant guild owner ID for an element, or undefined
    function getOwnerId(e) {
        var owner = getOwnerInstance(e);
        if (owner === undefined) {
            return undefined;
        }

        try {
            return owner.props.guild.ownerId;
        } catch (err) {
            // Catch TypeError if no guild in props
        }

        try {
            return owner.state.guild.ownerId;
        } catch (err) {
            // Catch TypeError if no guild in state
        }

        return undefined;
    }

    function addTag() {
        /* jshint validthis: true */
        var color = getUserColor(this);
        var tag = $("<span>", {
            class: "kawaii-tag",
        }).text("OWNER");

        if (color !== null) {
            tag.css("background-color", color);

            if (isBright(color)) {
                tag.addClass("kawaii-tag-bright");
            }
        }
        return tag;
    }

    var prevGuildId;

    function processServer(mutation) {
        var chat, guildId, ownerId, members, authors, tags;

        chat = $(".chat")[0];

        // Get the ID of the server
        guildId = getGuildId(chat);

        // Get the ID of the server's owner
        ownerId = getOwnerId(chat);
        if (ownerId === undefined) {
            // (Probably) not looking at a server
            return;
        }

        // Check if changed servers and need to redo member list tagging
        // React likes to make minimal changes to the DOM, so owner tags
        // will stick around (or not get added) when a user is in both this
        // and the previous server.
        if (guildId !== prevGuildId) {
            // Get all visible members
            members = $(".member-username-inner");
            // Remove tags that were added
            members.siblings(".kawaii-tag").remove();
            members.filter(".kawaii-tagged").removeClass("kawaii-tagged");
        } else {
            members = mutationFind(mutation, ".member-username-inner");
        }

        // Get the set of message authors affected by this mutation
        authors = mutationFind(mutation, ".user-name");

        if (!authors.closest(".message-group").hasClass("compact")) {
            // If not in compact mode, process the same as guild members
            members = members.add(authors);
        } else {
            // Process authors (tag before of name in compact mode)
            authors.filter((_, e) => getUserId(e) === ownerId).not(".kawaii-tagged")
                .before(addTag)
                .addClass("kawaii-tagged");
        }

        // Process guild members
        members.filter((_, e) => getUserId(e) === ownerId).not(".kawaii-tagged")
            .after(addTag)
            .addClass("kawaii-tagged");

        // User profile popout
        tags = mutationFind(mutation, ".discord-tag")
            .filter((_, e) => getUserId(e) === ownerId).not(".kawaii-tagged");

        // Small popout
        if (tags.parent(".username-wrapper").length > 0) {
            tags.append($("<span>", {class: "kawaii-tag kawaii-tag-invert"})
                .text("OWNER"))
                .addClass("kawaii-tagged");
        }

        // Fullscreen popout (modal)
        if (tags.parent(".header-info-inner").length > 0) {
            tags.append($("<span>", {class: "kawaii-tag kawaii-tag"})
                .text("OWNER"))
                .addClass("kawaii-tagged");
        }

        prevGuildId = guildId;
    }

    function processProfile(mutation) {
        var profile, userId, guilds;

        profile = mutationFind(mutation, "#user-profile-modal");
        userId = getUserId(profile[0]);
        if (userId === undefined) {
            // (Probably) not looking at a profile
            return;
        }

        guilds = profile.find(".guild .avatar-large");

        guilds.filter((_, e) => getOwnerId(e) === userId).parent().not(".kawaii-tagged")
            .append($("<span>", {class: "kawaii-tag"}).text("OWNER"))
            .addClass("kawaii-tagged");
    }

    // Helper function for finding all elements matching selector affected by a mutation
    function mutationFind(mutation, selector) {
        var target = $(mutation.target), addedNodes = $(mutation.addedNodes);
        var mutated = target.add(addedNodes).filter(selector);
        var descendants = addedNodes.find(selector);
        var ancestors = target.parents(selector);
        return mutated.add(descendants).add(ancestors);
    }

    // Default colors (https://discordapp.com/branding)
    // #7289DA - "Blurple"
    // #23272A - "Not quite black"
    var css = `
    .kawaii-tag {
        background: #7289da;
        font-size: 10px;
        font-weight: 600;
        color: #fff!important;
        margin-left: 6px;
        padding: 1px 2px;
        border-radius: 3px;
        text-transform: uppercase;
        vertical-align: bottom;
        line-height: 16px;
        -ms-flex-negative: 0;
        flex-shrink: 0;
    }

    .compact .kawaii-tag {
        margin: 0 3px 0 0;
    }

    .kawaii-tag-bright {
        color: #23272A!important;
    }

    .kawaii-tag-invert {
        background: #fff;
        color: #7289da!important;
    }`;

    ownerTag.prototype.start = function () {
        BdApi.injectCSS("kawaii-tag-css", css);
        // process entire document
        var mutation = {target: document, addedNodes: [document]};
        processServer(mutation);
        processProfile(mutation);
    };

    ownerTag.prototype.observer = function (mutation) {
        processServer(mutation);
        processProfile(mutation);
    };

    ownerTag.prototype.load = function () {};

    ownerTag.prototype.unload = function () {};

    ownerTag.prototype.stop = function () {
        BdApi.clearCSS("kawaii-tag-css");
        // Remove tags that were added
        $(".kawaii-tag").remove();
        $(".kawaii-tagged").removeClass("kawaii-tagged");
    };

    ownerTag.prototype.getSettingsPanel = function () {
        return "";
    };

    ownerTag.prototype.getName = function () {
        return "Owner Tags";
    };

    ownerTag.prototype.getDescription = function () {
        return "Show a tag next to a server owner's name";
    };

    ownerTag.prototype.getVersion = function () {
        return "1.3.1";
    };

    ownerTag.prototype.getAuthor = function () {
        return "noodlebox";
    };
})();

/*@end @*/
<commit_msg>Bump OwnerTag version to 1.3.2<commit_after>//META{"name":"ownerTag"}*//

/*@cc_on
@if (@_jscript)
    // Offer to self-install for clueless users that try to run this directly.
    var shell = WScript.CreateObject("WScript.Shell");
    var fs = new ActiveXObject("Scripting.FileSystemObject");
    var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins");
    var pathSelf = WScript.ScriptFullName;
    // Put the user at ease by addressing them in the first person
    shell.Popup("It looks like you mistakenly tried to run me directly. (don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
    if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
        shell.Popup("I'm in the correct folder already.\nJust reload Discord with Ctrl+R.", 0, "I'm already installed", 0x40);
    } else if (!fs.FolderExists(pathPlugins)) {
        shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
    } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
        fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
        // Show the user where to put plugins in the future
        shell.Exec("explorer " + pathPlugins);
        shell.Popup("I'm installed!\nJust reload Discord with Ctrl+R.", 0, "Successfully installed", 0x40);
    }
    WScript.Quit();
@else @*/

var ownerTag = function () {};

(function () {
    "use strict";

    // This is super hackish, and will likely break as Discord's internal API changes
    // Anything using this or what it returns should be prepared to catch some exceptions
    const getInternalInstance = e => e[Object.keys(e).find(k => k.startsWith("__reactInternalInstance"))];

    function getOwnerInstance(e, {include, exclude=["Popout", "Tooltip", "Scroller", "BackgroundFlash"]} = {}) {
        if (e === undefined) {
            return undefined;
        }

        // Set up filter; if no include filter is given, match all except those in exclude
        const excluding = include === undefined;
        const filter = excluding ? exclude : include;

        // Get displayName of the React class associated with this element
        // Based on getName(), but only check for an explicit displayName
        function getDisplayName(owner) {
            const type = owner._currentElement.type;
            const constructor = owner._instance && owner._instance.constructor;
            return type.displayName || constructor && constructor.displayName || null;
        }
        // Check class name against filters
        function classFilter(owner) {
            const name = getDisplayName(owner);
            return (name !== null && !!(filter.includes(name) ^ excluding));
        }

        // Walk up the hierarchy until a proper React object is found
        for (let prev, curr=getInternalInstance(e); !_.isNil(curr); prev=curr, curr=curr._hostParent) {
            // Before checking its parent, try to find a React object for prev among renderedChildren
            // This finds React objects which don't have a direct counterpart in the DOM hierarchy
            // e.g. Message, ChannelMember, ...
            if (prev !== undefined && !_.isNil(curr._renderedChildren)) {
                /* jshint loopfunc: true */
                let owner = Object.values(curr._renderedChildren)
                    .find(v => !_.isNil(v._instance) && v.getHostNode() === prev.getHostNode());
                if (!_.isNil(owner) && classFilter(owner)) {
                    return owner._instance;
                }
            }

            if (_.isNil(curr._currentElement)) {
                continue;
            }

            // Get a React object if one corresponds to this DOM element
            // e.g. .user-popout -> UserPopout, ...
            let owner = curr._currentElement._owner;
            if (!_.isNil(owner) && classFilter(owner)) {
                return owner._instance;
            }
        }

        return null;
    }

    function getInternalProps(e) {
        if (e === undefined) {
            return undefined;
        }

        try {
            return getOwnerInstance(e).props;
        } catch (err) {
            return undefined;
        }
    }

    // Get the relevant user ID for an element, or undefined
    function getUserId(e) {
        var props = getInternalProps(e);
        if (props === undefined) {
            return undefined;
        }

        try {
            return props.user.id;
        } catch (err) {
            // Catch TypeError if no user in props
        }

        try {
            return props.message.author.id;
        } catch (err) {
            // Catch TypeError if no message in props
        }

        return undefined;
    }

    // Get the relevant user color for an element, or undefined
    function getUserColor(e) {
        var props = getInternalProps(e);
        if (props === undefined) {
            return undefined;
        }

        try {
            return props.message.colorString;
        } catch (err) {
            // Catch TypeError if no message in props
        }

        // Return colorString or undefined if not present
        return props.colorString;
    }

    // Convert colorString to array of RGB values
    function hexToRgb(hex) {
        var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? [
            parseInt(result[1], 16),
            parseInt(result[2], 16),
            parseInt(result[3], 16)
        ] : null;
    }

    // Check whether colorString is brighter than middle gray
    // See: https://stackoverflow.com/a/3943023/6807290
    function isBright(color){
        var c = hexToRgb(color);
        return !!c && (c[0]*0.299 + c[1]*0.587 + c[2]*0.114) > 186;
    }

    // Get the relevant guild ID for an element, or undefined
    function getGuildId(e) {
        var owner = getOwnerInstance(e);
        if (owner === undefined) {
            return undefined;
        }

        try {
            return owner.props.guild.id;
        } catch (err) {
            // Catch TypeError if no guild in props
        }

        try {
            return owner.state.guild.id;
        } catch (err) {
            // Catch TypeError if no guild in state
        }

        return undefined;
    }

    // Get the relevant guild owner ID for an element, or undefined
    function getOwnerId(e) {
        var owner = getOwnerInstance(e);
        if (owner === undefined) {
            return undefined;
        }

        try {
            return owner.props.guild.ownerId;
        } catch (err) {
            // Catch TypeError if no guild in props
        }

        try {
            return owner.state.guild.ownerId;
        } catch (err) {
            // Catch TypeError if no guild in state
        }

        return undefined;
    }

    function addTag() {
        /* jshint validthis: true */
        var color = getUserColor(this);
        var tag = $("<span>", {
            class: "kawaii-tag",
        }).text("OWNER");

        if (color !== null) {
            tag.css("background-color", color);

            if (isBright(color)) {
                tag.addClass("kawaii-tag-bright");
            }
        }
        return tag;
    }

    var prevGuildId;

    function processServer(mutation) {
        var chat, guildId, ownerId, members, authors, tags;

        chat = $(".chat")[0];

        // Get the ID of the server
        guildId = getGuildId(chat);

        // Get the ID of the server's owner
        ownerId = getOwnerId(chat);
        if (ownerId === undefined) {
            // (Probably) not looking at a server
            return;
        }

        // Check if changed servers and need to redo member list tagging
        // React likes to make minimal changes to the DOM, so owner tags
        // will stick around (or not get added) when a user is in both this
        // and the previous server.
        if (guildId !== prevGuildId) {
            // Get all visible members
            members = $(".member-username-inner");
            // Remove tags that were added
            members.siblings(".kawaii-tag").remove();
            members.filter(".kawaii-tagged").removeClass("kawaii-tagged");
        } else {
            members = mutationFind(mutation, ".member-username-inner");
        }

        // Get the set of message authors affected by this mutation
        authors = mutationFind(mutation, ".user-name");

        if (!authors.closest(".message-group").hasClass("compact")) {
            // If not in compact mode, process the same as guild members
            members = members.add(authors);
        } else {
            // Process authors (tag before of name in compact mode)
            authors.filter((_, e) => getUserId(e) === ownerId).not(".kawaii-tagged")
                .before(addTag)
                .addClass("kawaii-tagged");
        }

        // Process guild members
        members.filter((_, e) => getUserId(e) === ownerId).not(".kawaii-tagged")
            .after(addTag)
            .addClass("kawaii-tagged");

        // User profile popout
        tags = mutationFind(mutation, ".discord-tag")
            .filter((_, e) => getUserId(e) === ownerId).not(".kawaii-tagged");

        // Small popout
        if (tags.parent(".username-wrapper").length > 0) {
            tags.append($("<span>", {class: "kawaii-tag kawaii-tag-invert"})
                .text("OWNER"))
                .addClass("kawaii-tagged");
        }

        // Fullscreen popout (modal)
        if (tags.parent(".header-info-inner").length > 0) {
            tags.append($("<span>", {class: "kawaii-tag kawaii-tag"})
                .text("OWNER"))
                .addClass("kawaii-tagged");
        }

        prevGuildId = guildId;
    }

    function processProfile(mutation) {
        var profile, userId, guilds;

        profile = mutationFind(mutation, "#user-profile-modal");
        userId = getUserId(profile[0]);
        if (userId === undefined) {
            // (Probably) not looking at a profile
            return;
        }

        guilds = profile.find(".guild .avatar-large");

        guilds.filter((_, e) => getOwnerId(e) === userId).parent().not(".kawaii-tagged")
            .append($("<span>", {class: "kawaii-tag"}).text("OWNER"))
            .addClass("kawaii-tagged");
    }

    // Helper function for finding all elements matching selector affected by a mutation
    function mutationFind(mutation, selector) {
        var target = $(mutation.target), addedNodes = $(mutation.addedNodes);
        var mutated = target.add(addedNodes).filter(selector);
        var descendants = addedNodes.find(selector);
        var ancestors = target.parents(selector);
        return mutated.add(descendants).add(ancestors);
    }

    // Default colors (https://discordapp.com/branding)
    // #7289DA - "Blurple"
    // #23272A - "Not quite black"
    var css = `
    .kawaii-tag {
        background: #7289da;
        font-size: 10px;
        font-weight: 600;
        color: #fff!important;
        margin-left: 6px;
        padding: 1px 2px;
        border-radius: 3px;
        text-transform: uppercase;
        vertical-align: bottom;
        line-height: 16px;
        -ms-flex-negative: 0;
        flex-shrink: 0;
    }

    .compact .kawaii-tag {
        margin: 0 3px 0 0;
    }

    .kawaii-tag-bright {
        color: #23272A!important;
    }

    .kawaii-tag-invert {
        background: #fff;
        color: #7289da!important;
    }`;

    ownerTag.prototype.start = function () {
        BdApi.injectCSS("kawaii-tag-css", css);
        // process entire document
        var mutation = {target: document, addedNodes: [document]};
        processServer(mutation);
        processProfile(mutation);
    };

    ownerTag.prototype.observer = function (mutation) {
        processServer(mutation);
        processProfile(mutation);
    };

    ownerTag.prototype.load = function () {};

    ownerTag.prototype.unload = function () {};

    ownerTag.prototype.stop = function () {
        BdApi.clearCSS("kawaii-tag-css");
        // Remove tags that were added
        $(".kawaii-tag").remove();
        $(".kawaii-tagged").removeClass("kawaii-tagged");
    };

    ownerTag.prototype.getSettingsPanel = function () {
        return "";
    };

    ownerTag.prototype.getName = function () {
        return "Owner Tags";
    };

    ownerTag.prototype.getDescription = function () {
        return "Show a tag next to a server owner's name";
    };

    ownerTag.prototype.getVersion = function () {
        return "1.3.2";
    };

    ownerTag.prototype.getAuthor = function () {
        return "noodlebox";
    };
})();

/*@end @*/
<|endoftext|>


<commit_before>/* Copyright (c) 2012 the authors listed at the following URL, and/or
the authors of referenced articles or incorporated external code:
http://en.literateprograms.org/Quickhull_(Javascript)?action=history&offset=20120410175256

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Retrieved from: http://en.literateprograms.org/Quickhull_(Javascript)?oldid=18434
*/

(function () {
	L.QuickHull = {
		getDistant: function (cpt, bl) {
			var Vy = bl[1][0] - bl[0][0];
			var Vx = bl[0][1] - bl[1][1];
			return (Vx * (cpt[0] - bl[0][0]) + Vy * (cpt[1] - bl[0][1]))
		},

		findMostDistantPointFromBaseLine: function (baseLine, points) {
			var maxD = 0;
			var maxPt = new Array();
			var newPoints = new Array();
			for (var idx in points) {
				var pt = points[idx];
				var d = this.getDistant(pt, baseLine);

				if (d > 0) {
					newPoints.push(pt);
				} else {
					continue;
				}

				if (d > maxD) {
					maxD = d;
					maxPt = pt;
				}

			}
			return { 'maxPoint': maxPt, 'newPoints': newPoints }
		},

		buildConvexHull: function (baseLine, points) {
			var convexHullBaseLines = new Array();
			var t = this.findMostDistantPointFromBaseLine(baseLine, points);
			if (t.maxPoint.length) { // if there is still a point "outside" the base line
				convexHullBaseLines =
					convexHullBaseLines.concat(
						this.buildConvexHull([baseLine[0], t.maxPoint], t.newPoints)
					);
				convexHullBaseLines =
					convexHullBaseLines.concat(
						this.buildConvexHull([t.maxPoint, baseLine[1]], t.newPoints)
					);
				return convexHullBaseLines;
			} else {  // if there is no more point "outside" the base line, the current base line is part of the convex hull
				return [baseLine];
			}
		},

		getConvexHull: function (points) {
			//find first baseline
			var maxX, minX;
			var maxPt, minPt;
			for (var idx in points) {
				var pt = points[idx];
				if (pt[0] > maxX || !maxX) {
					maxPt = pt;
					maxX = pt[0];
				}
				if (pt[0] < minX || !minX) {
					minPt = pt;
					minX = pt[0];
				}
			}
			var ch = [].concat(this.buildConvexHull([minPt, maxPt], points),
								this.buildConvexHull([maxPt, minPt], points))
			return ch;
		}
	}
}());

L.MarkerCluster.include({
	getConvexHull: function () {
		var childMarkers = this.getAllChildMarkers(),
			points = [],
			hullLatLng = [],
			hull, p, i;

		for (i = childMarkers.length - 1; i >= 0; i--) {
			p = childMarkers[i].getLatLng();
			points.push([p.lat, p.lng]);
		}

		hull = L.QuickHull.getConvexHull(points);

		for (i = 0; i < hull.length; i++) {
			p = hull[i];
			hullLatLng.push(new L.LatLng(p[0][0], p[0][1]));
		}

		return hullLatLng;
	}
});<commit_msg>tidy up JS<commit_after>/* Copyright (c) 2012 the authors listed at the following URL, and/or
the authors of referenced articles or incorporated external code:
http://en.literateprograms.org/Quickhull_(Javascript)?action=history&offset=20120410175256

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Retrieved from: http://en.literateprograms.org/Quickhull_(Javascript)?oldid=18434
*/

(function () {
	L.QuickHull = {
		getDistant: function (cpt, bl) {
			var vY = bl[1][0] - bl[0][0];
			var vX = bl[0][1] - bl[1][1];
			return (vX * (cpt[0] - bl[0][0]) + vY * (cpt[1] - bl[0][1]));
		},

		findMostDistantPointFromBaseLine: function (baseLine, points) {
			var maxD = 0;
			var maxPt = new Array();
			var newPoints = new Array();
			for (var idx in points) {
				var pt = points[idx];
				var d = this.getDistant(pt, baseLine);

				if (d > 0) {
					newPoints.push(pt);
				} else {
					continue;
				}

				if (d > maxD) {
					maxD = d;
					maxPt = pt;
				}

			}
			return { 'maxPoint': maxPt, 'newPoints': newPoints };
		},

		buildConvexHull: function (baseLine, points) {
			var convexHullBaseLines = new Array();
			var t = this.findMostDistantPointFromBaseLine(baseLine, points);
			if (t.maxPoint.length) { // if there is still a point "outside" the base line
				convexHullBaseLines =
					convexHullBaseLines.concat(
						this.buildConvexHull([baseLine[0], t.maxPoint], t.newPoints)
					);
				convexHullBaseLines =
					convexHullBaseLines.concat(
						this.buildConvexHull([t.maxPoint, baseLine[1]], t.newPoints)
					);
				return convexHullBaseLines;
			} else {  // if there is no more point "outside" the base line, the current base line is part of the convex hull
				return [baseLine];
			}
		},

		getConvexHull: function (points) {
			//find first baseline
			var maxX = false, minX = false;
			var maxPt = null, minPt = null;

			for (var idx in points) {
				var pt = points[idx];
				if (maxX === false || pt[0] > maxX) {
					maxPt = pt;
					maxX = pt[0];
				}
				if (minX === false || pt[0] < minX) {
					minPt = pt;
					minX = pt[0];
				}
			}
			var ch = [].concat(this.buildConvexHull([minPt, maxPt], points),
								this.buildConvexHull([maxPt, minPt], points));
			return ch;
		}
	};
}());

L.MarkerCluster.include({
	getConvexHull: function () {
		var childMarkers = this.getAllChildMarkers(),
			points = [],
			hullLatLng = [],
			hull, p, i;

		for (i = childMarkers.length - 1; i >= 0; i--) {
			p = childMarkers[i].getLatLng();
			points.push([p.lat, p.lng]);
		}

		hull = L.QuickHull.getConvexHull(points);

		for (i = 0; i < hull.length; i++) {
			p = hull[i];
			hullLatLng.push(new L.LatLng(p[0][0], p[0][1]));
		}

		return hullLatLng;
	}
});<|endoftext|>


<commit_before>//META{"name":"ownerTag"}*//

var ownerTag = function () {};

(function () {
    "use strict";

    // This is super hackish, and will likely break as Discord's internal API changes
    // Anything using this or what it returns should be prepared to catch some exceptions
    function getInternalProps(e) {
        try {
            var reactInternal = e[Object.keys(e).filter(k => k.startsWith("__reactInternalInstance"))[0]];
            return reactInternal._currentElement._owner._instance.props;
        } catch (err) {
            return undefined;
        }
    }

    // Get the relevant user ID for an element, or undefined
    function getUserId(e) {
        var props = getInternalProps(e);
        if (props === undefined) {
            return undefined;
        }

        try {
            return props.user.id;
        } catch (err) {
            // Catch TypeError if no user in props
        }

        try {
            return props.message.author.id;
        } catch (err) {
            // Catch TypeError if no message in props
        }

        return undefined;
    }

    // Get the relevant guild owner ID for an element, or undefined
    function getOwnerId(e) {
        var props = getInternalProps(e);
        if (props === undefined) {
            return undefined;
        }

        try {
            return props.guild.ownerId;
        } catch (err) {
            // Catch TypeError if no guild in props
        }

        return undefined;
    }

    function processServer(mutation) {
        var ownerId, usernames, tags;

        // Get the ID of the server's owner
        ownerId = getOwnerId($(".guild.selected")[0]);
        if (ownerId === undefined) {
            // (Probably) not looking at a server
            return;
        }

        // Check if changed servers and need to redo member list tagging
        // React likes to make minimal changes to the DOM, so owner tags
        // will stick around (or not get added) when a user is in both this
        // and the previous server.
        if (ownerId !== prevOwnerId) {
            // Get all visible members
            usernames = $(".member-username-inner");
            // Remove tags that were added
            usernames.siblings(".kawaii-tag").remove();
            usernames.filter(".kawaii-tagged").removeClass("kawaii-tagged");
            // Add the set of message authors affected by this mutation
            usernames = usernames.add(mutationFind(mutation, ".user-name"));
        } else {
            // Get the set of message authors and server members affected by this mutation
            usernames = mutationFind(mutation, ".member-username-inner, .user-name");
        }

        // Process usernames
        usernames.filter((_, e) => getUserId(e) === ownerId).not(".kawaii-tagged")
            .after($("<span>", {class: "bot-tag kawaii-tag"}).text("OWNER"))
            .addClass("kawaii-tagged");

        tags = mutationFind(mutation, ".discord-tag");

        tags.filter((_, e) => getUserId(e) === ownerId).not(".kawaii-tagged")
            .append($("<span>", {class: "bot-tag bot-tag-invert kawaii-tag"}).text("OWNER"))
            .addClass("kawaii-tagged");

        prevOwnerId = ownerId;
    }

    function processProfile(mutation) {
        var profile, userId, guilds;

        profile = mutationFind(mutation, "#user-profile-modal");
        userId = getUserId(profile[0]);
        if (userId === undefined) {
            // (Probably) not looking at a profile
            return;
        }

        guilds = profile.find(".guild .avatar-large");

        guilds.filter((_, e) => getOwnerId(e) === userId).parent().not(".kawaii-tagged")
            .append($("<span>", {class: "bot-tag kawaii-tag"}).text("OWNER"))
            .addClass("kawaii-tagged");
    }

    // Helper function for finding all elements matching selector affected by a mutation
    function mutationFind(mutation, selector) {
        var target = $(mutation.target), addedNodes = $(mutation.addedNodes);
        var mutated = target.add(addedNodes).filter(selector);
        var descendants = addedNodes.find(selector);
        var ancestors = target.parents(selector);
        return mutated.add(descendants).add(ancestors);
    }

    var prevOwnerId;

    ownerTag.prototype.start = function () {
        // process entire document
        var mutation = {target: document};
        processServer(mutation);
        processProfile(mutation);
    };

    ownerTag.prototype.observer = function (mutation) {
        processServer(mutation);
        processProfile(mutation);
    };

    ownerTag.prototype.load = function () {};

    ownerTag.prototype.unload = function () {};

    ownerTag.prototype.stop = function () {
        // Remove tags that were added
        $(".kawaii-tag").remove();
        $(".kawaii-tagged").removeClass("kawaii-tagged");
    };

    ownerTag.prototype.getSettingsPanel = function () {
        return "";
    };

    ownerTag.prototype.getName = function () {
        return "Owner Tags";
    };

    ownerTag.prototype.getDescription = function () {
        return "Show a tag next to a server owner's name";
    };

    ownerTag.prototype.getVersion = function () {
        return "1.0.2";
    };

    ownerTag.prototype.getAuthor = function () {
        return "noodlebox";
    };
})();
<commit_msg>Bump OwnerTag version to 1.1.0<commit_after>//META{"name":"ownerTag"}*//

var ownerTag = function () {};

(function () {
    "use strict";

    // This is super hackish, and will likely break as Discord's internal API changes
    // Anything using this or what it returns should be prepared to catch some exceptions
    function getInternalProps(e) {
        try {
            var reactInternal = e[Object.keys(e).filter(k => k.startsWith("__reactInternalInstance"))[0]];
            return reactInternal._currentElement._owner._instance.props;
        } catch (err) {
            return undefined;
        }
    }

    // Get the relevant user ID for an element, or undefined
    function getUserId(e) {
        var props = getInternalProps(e);
        if (props === undefined) {
            return undefined;
        }

        try {
            return props.user.id;
        } catch (err) {
            // Catch TypeError if no user in props
        }

        try {
            return props.message.author.id;
        } catch (err) {
            // Catch TypeError if no message in props
        }

        return undefined;
    }

    // Get the relevant guild owner ID for an element, or undefined
    function getOwnerId(e) {
        var props = getInternalProps(e);
        if (props === undefined) {
            return undefined;
        }

        try {
            return props.guild.ownerId;
        } catch (err) {
            // Catch TypeError if no guild in props
        }

        return undefined;
    }

    function processServer(mutation) {
        var ownerId, usernames, tags;

        // Get the ID of the server's owner
        ownerId = getOwnerId($(".guild.selected")[0]);
        if (ownerId === undefined) {
            // (Probably) not looking at a server
            return;
        }

        // Check if changed servers and need to redo member list tagging
        // React likes to make minimal changes to the DOM, so owner tags
        // will stick around (or not get added) when a user is in both this
        // and the previous server.
        if (ownerId !== prevOwnerId) {
            // Get all visible members
            usernames = $(".member-username-inner");
            // Remove tags that were added
            usernames.siblings(".kawaii-tag").remove();
            usernames.filter(".kawaii-tagged").removeClass("kawaii-tagged");
            // Add the set of message authors affected by this mutation
            usernames = usernames.add(mutationFind(mutation, ".user-name"));
        } else {
            // Get the set of message authors and server members affected by this mutation
            usernames = mutationFind(mutation, ".member-username-inner, .user-name");
        }

        // Process usernames
        usernames.filter((_, e) => getUserId(e) === ownerId).not(".kawaii-tagged")
            .after($("<span>", {class: "bot-tag kawaii-tag"}).text("OWNER"))
            .addClass("kawaii-tagged");

        tags = mutationFind(mutation, ".discord-tag");

        tags.filter((_, e) => getUserId(e) === ownerId).not(".kawaii-tagged")
            .append($("<span>", {class: "bot-tag bot-tag-invert kawaii-tag"}).text("OWNER"))
            .addClass("kawaii-tagged");

        prevOwnerId = ownerId;
    }

    function processProfile(mutation) {
        var profile, userId, guilds;

        profile = mutationFind(mutation, "#user-profile-modal");
        userId = getUserId(profile[0]);
        if (userId === undefined) {
            // (Probably) not looking at a profile
            return;
        }

        guilds = profile.find(".guild .avatar-large");

        guilds.filter((_, e) => getOwnerId(e) === userId).parent().not(".kawaii-tagged")
            .append($("<span>", {class: "bot-tag kawaii-tag"}).text("OWNER"))
            .addClass("kawaii-tagged");
    }

    // Helper function for finding all elements matching selector affected by a mutation
    function mutationFind(mutation, selector) {
        var target = $(mutation.target), addedNodes = $(mutation.addedNodes);
        var mutated = target.add(addedNodes).filter(selector);
        var descendants = addedNodes.find(selector);
        var ancestors = target.parents(selector);
        return mutated.add(descendants).add(ancestors);
    }

    var prevOwnerId;

    ownerTag.prototype.start = function () {
        // process entire document
        var mutation = {target: document};
        processServer(mutation);
        processProfile(mutation);
    };

    ownerTag.prototype.observer = function (mutation) {
        processServer(mutation);
        processProfile(mutation);
    };

    ownerTag.prototype.load = function () {};

    ownerTag.prototype.unload = function () {};

    ownerTag.prototype.stop = function () {
        // Remove tags that were added
        $(".kawaii-tag").remove();
        $(".kawaii-tagged").removeClass("kawaii-tagged");
    };

    ownerTag.prototype.getSettingsPanel = function () {
        return "";
    };

    ownerTag.prototype.getName = function () {
        return "Owner Tags";
    };

    ownerTag.prototype.getDescription = function () {
        return "Show a tag next to a server owner's name";
    };

    ownerTag.prototype.getVersion = function () {
        return "1.1.0";
    };

    ownerTag.prototype.getAuthor = function () {
        return "noodlebox";
    };
})();
<|endoftext|>


<commit_before>//main functions
var helper = require('./lib/shared');

//libs
var Myo = require('myo');
var ioc = require('socket.io-client');
var socket = ioc.connect('http://localhost:3000');

var move = false;
var center_x = 23;
var last_yaw = 0;

Myo.connect('do.d3.skol');

Myo.on('connected', () => {
    helper.logger.debug('[Myo] Connected');
    Myo.setLockingPolicy('none');
});

Myo.on('pose', (pose) => {
    helper.logger.debug(`[MYO] ${pose}`);
    if (pose === 'fist' || pose === 'fingers_spread') {
        socket.emit('myo', {
            type: 'pose',
            pose: pose
        });
    } else if (pose === 'double_tap') {
        if (move) {
            move = false
        } else {
            center_x = 23;
            move = true;
        }
    } else if (pose === 'wave_in') {
        socket.emit('animation', {
            animation: 'Music',
            type: 'boom'
        });
    } else if (pose === 'wave_out') {
        socket.emit('animation', {
            animation: 'Music',
            type: 'long_boom'
        });
    }
});

Myo.on('orientation', (data) => {

    var x = data.x;
    var y = data.y;
    var z = data.z;
    var w = data.w;

    //vertical
    var pitch = Math.asin(Math.max(-1.0, Math.min(1.0, 2.0 * (w * y - z * x))));
    var pitch_w = ((pitch + Math.PI / 2.0) / Math.PI * 180);

    //horizontal
    var yaw = Math.atan2(2.0 * (w * z + x * y), 1.0 - 2.0 * (y * y + z * z));
    var yaw_w = ((yaw + Math.PI) / (Math.PI * 2.0) * 360);

    //rotation
    var roll = Math.atan2(2.0 * (w * x + y * z), 1.0 - 2.0 * (x * x + y * y));
    var roll_w = ((roll + Math.PI) / (Math.PI * 2.0) * 40);

    y = parseInt((pitch_w - 90) / 4);

    if (yaw_w - 4 > last_yaw) {
        center_x++;
        last_yaw = yaw_w;
    } else if (yaw_w + 4 < last_yaw) {
        center_x--;
        last_yaw = yaw_w;
    }
    if (center_x == 80) {
        center_x = 0;
    } else if (center_x == -1) {
        center_x = 79;
    }
    if (move) {
        socket.emit('myo', {
            type: 'move',
            x: center_x,
            y: y
        });
    }
});
<commit_msg>Update with a myo local tester<commit_after>//main functions
var helper = require('./lib/shared');

//libs
var Myo = require('myo');
var ioc = require('socket.io-client');
var socket = ioc.connect('http://192.168.42.10:3000');

var move = false;
var center_x = 23;
var last_yaw = 0;

Myo.connect('do.d3.skol');

Myo.on('connected', () => {
    helper.logger.debug('[Myo] Connected');
    Myo.setLockingPolicy('none');
});

Myo.on('pose', (pose) => {
    helper.logger.debug(`[MYO] ${pose}`);
    if (pose === 'fist' || pose === 'fingers_spread') {
        socket.emit('myo', {
            type: 'pose',
            pose: pose
        });
    } else if (pose === 'double_tap') {
        if (move) {
            move = false
        } else {
            center_x = 23;
            move = true;
        }
    } else if (pose === 'wave_in') {
        socket.emit('animation', {
            animation: 'Music',
            type: 'boom'
        });
    } else if (pose === 'wave_out') {
        socket.emit('animation', {
            animation: 'Music',
            type: 'long_boom'
        });
    }
});

Myo.on('orientation', (data) => {

    var x = data.x;
    var y = data.y;
    var z = data.z;
    var w = data.w;

    //vertical
    var pitch = Math.asin(Math.max(-1.0, Math.min(1.0, 2.0 * (w * y - z * x))));
    var pitch_w = ((pitch + Math.PI / 2.0) / Math.PI * 180);

    //horizontal
    var yaw = Math.atan2(2.0 * (w * z + x * y), 1.0 - 2.0 * (y * y + z * z));
    var yaw_w = ((yaw + Math.PI) / (Math.PI * 2.0) * 360);

    //rotation
    var roll = Math.atan2(2.0 * (w * x + y * z), 1.0 - 2.0 * (x * x + y * y));
    var roll_w = ((roll + Math.PI) / (Math.PI * 2.0) * 40);

    y = parseInt((pitch_w - 90) / 4);

    if (yaw_w - 4 > last_yaw) {
        center_x++;
        last_yaw = yaw_w;
    } else if (yaw_w + 4 < last_yaw) {
        center_x--;
        last_yaw = yaw_w;
    }
    if (center_x == 80) {
        center_x = 0;
    } else if (center_x == -1) {
        center_x = 79;
    }
    if (move) {
        socket.emit('myo', {
            type: 'move',
            x: center_x,
            y: y
        });
    }
});
<|endoftext|>


<commit_before>//META{"name":"ownerTag"}*//

var ownerTag = function () {};

(function () {
    "use strict";

    // This is super hackish, and will likely break as Discord's internal API changes
    // Anything using this or what it returns should be prepared to catch some exceptions
    function getInternalProps(e) {
        try {
            var reactInternal = e[Object.keys(e).filter(k => k.startsWith("__reactInternalInstance"))[0]];
            return reactInternal._currentElement._owner._instance.props;
        } catch (err) {
            return undefined;
        }
    }

    // Get the relevant user ID for an element, or undefined
    function getUserId(e) {
        var props = getInternalProps(e);
        if (props === undefined) {
            return undefined;
        }

        try {
            return props.user.id;
        } catch (err) {
            // Catch TypeError if no user in props
        }

        try {
            return props.message.author.id;
        } catch (err) {
            // Catch TypeError if no message in props
        }

        return undefined;
    }

    // Helper function for finding all elements matching selector affected by a mutation
    function mutationFind(mutation, selector) {
        var target = $(mutation.target), addedNodes = $(mutation.addedNodes);
        var mutated = target.add(addedNodes).filter(selector);
        var descendants = addedNodes.find(selector);
        var ancestors = target.parents(selector);
        return mutated.add(descendants).add(ancestors);
    }

    // Get user ID of the owner of the currently active server, or undefined
    function getOwnerId() {
        var server = $(".guild.selected")[0];
        if (server === undefined) {
            // Not looking at a server
            return undefined;
        }
        return getInternalProps(server).guild.ownerId;
    }

    // Add owner tags to all untagged elements
    function addTags(elements) {
        elements.not(".kawaii-tagged")
            .append($("<span>", {class: "bot-tag kawaii-tag"}).text("OWNER"))
            .addClass("kawaii-tagged");
    }

    var prevOwnerId;

    ownerTag.prototype.start = function () {
        // Get the ID of the server's owner
        var ownerId = getOwnerId();
        if (ownerId === undefined) {
            return;
        }

        // Get a set of message authors and server members
        var usernames = $(".member-username, .username-wrapper");

        // Process usernames
        addTags(usernames.filter((_, e) => getUserId(e) === ownerId));

        prevOwnerId = ownerId;
    };

    ownerTag.prototype.observer = function (mutation) {
        // Get the ID of the server's owner
        var ownerId = getOwnerId();
        if (ownerId === undefined) {
            return;
        }

        var usernames;
        // Check if changed servers and need to redo member list tagging
        // React likes to make minimal changes to the DOM, so owner tags
        // will stick around (or not get added) when a user is in both this
        // and the previous server.
        if (ownerId !== prevOwnerId) {
            // Get all visible members
            usernames = $(".member-username");
            // Remove tags that were added
            usernames.find(".kawaii-tag").remove();
            usernames.filter(".kawaii-tagged").removeClass("kawaii-tagged");
            // Add the set of message authors affected by this mutation
            usernames = usernames.add(mutationFind(mutation, ".username-wrapper"));
        } else {
            // Get the set of message authors and server members affected by this mutation
            usernames = mutationFind(mutation, ".member-username, .username-wrapper");
        }

        // Process usernames
        addTags(usernames.filter((_, e) => getUserId(e) === ownerId));

        prevOwnerId = ownerId;
    };

    ownerTag.prototype.load = function () {};

    ownerTag.prototype.unload = function () {};

    ownerTag.prototype.stop = function () {
        // Remove tags that were added
        $(".kawaii-tag").remove();
        $(".kawaii-tagged").removeClass("kawaii-tagged");
    };

    ownerTag.prototype.getSettingsPanel = function () {
        return "";
    };

    ownerTag.prototype.getName = function () {
        return "Owner Tags";
    };

    ownerTag.prototype.getDescription = function () {
        return "Show a tag next to a server owner's name";
    };

    ownerTag.prototype.getVersion = function () {
        return "1.0.2";
    };

    ownerTag.prototype.getAuthor = function () {
        return "noodlebox";
    };
})();
<commit_msg>Fix compact mode issues and add tags to profile<commit_after>//META{"name":"ownerTag"}*//

var ownerTag = function () {};

(function () {
    "use strict";

    // This is super hackish, and will likely break as Discord's internal API changes
    // Anything using this or what it returns should be prepared to catch some exceptions
    function getInternalProps(e) {
        try {
            var reactInternal = e[Object.keys(e).filter(k => k.startsWith("__reactInternalInstance"))[0]];
            return reactInternal._currentElement._owner._instance.props;
        } catch (err) {
            return undefined;
        }
    }

    // Get the relevant user ID for an element, or undefined
    function getUserId(e) {
        var props = getInternalProps(e);
        if (props === undefined) {
            return undefined;
        }

        try {
            return props.user.id;
        } catch (err) {
            // Catch TypeError if no user in props
        }

        try {
            return props.message.author.id;
        } catch (err) {
            // Catch TypeError if no message in props
        }

        return undefined;
    }

    // Get the relevant guild owner ID for an element, or undefined
    function getOwnerId(e) {
        var props = getInternalProps(e);
        if (props === undefined) {
            return undefined;
        }

        try {
            return props.guild.ownerId;
        } catch (err) {
            // Catch TypeError if no guild in props
        }

        return undefined;
    }

    function processServer(mutation) {
        var ownerId, usernames, tags;

        // Get the ID of the server's owner
        ownerId = getOwnerId($(".guild.selected")[0]);
        if (ownerId === undefined) {
            // (Probably) not looking at a server
            return;
        }

        // Check if changed servers and need to redo member list tagging
        // React likes to make minimal changes to the DOM, so owner tags
        // will stick around (or not get added) when a user is in both this
        // and the previous server.
        if (ownerId !== prevOwnerId) {
            // Get all visible members
            usernames = $(".member-username-inner");
            // Remove tags that were added
            usernames.siblings(".kawaii-tag").remove();
            usernames.filter(".kawaii-tagged").removeClass("kawaii-tagged");
            // Add the set of message authors affected by this mutation
            usernames = usernames.add(mutationFind(mutation, ".user-name"));
        } else {
            // Get the set of message authors and server members affected by this mutation
            usernames = mutationFind(mutation, ".member-username-inner, .user-name");
        }

        // Process usernames
        usernames.filter((_, e) => getUserId(e) === ownerId).not(".kawaii-tagged")
            .after($("<span>", {class: "bot-tag kawaii-tag"}).text("OWNER"))
            .addClass("kawaii-tagged");

        tags = mutationFind(mutation, ".discord-tag");

        tags.filter((_, e) => getUserId(e) === ownerId).not(".kawaii-tagged")
            .append($("<span>", {class: "bot-tag bot-tag-invert kawaii-tag"}).text("OWNER"))
            .addClass("kawaii-tagged");

        prevOwnerId = ownerId;
    }

    function processProfile(mutation) {
        var profile, userId, guilds;

        profile = mutationFind(mutation, "#user-profile-modal");
        userId = getUserId(profile[0]);
        if (userId === undefined) {
            // (Probably) not looking at a profile
            return;
        }

        guilds = profile.find(".guild .avatar-large");

        guilds.filter((_, e) => getOwnerId(e) === userId).parent().not(".kawaii-tagged")
            .append($("<span>", {class: "bot-tag kawaii-tag"}).text("OWNER"))
            .addClass("kawaii-tagged");
    }

    // Helper function for finding all elements matching selector affected by a mutation
    function mutationFind(mutation, selector) {
        var target = $(mutation.target), addedNodes = $(mutation.addedNodes);
        var mutated = target.add(addedNodes).filter(selector);
        var descendants = addedNodes.find(selector);
        var ancestors = target.parents(selector);
        return mutated.add(descendants).add(ancestors);
    }

    var prevOwnerId;

    ownerTag.prototype.start = function () {
        // process entire document
        var mutation = {target: document};
        processServer(mutation);
        processProfile(mutation);
    };

    ownerTag.prototype.observer = function (mutation) {
        processServer(mutation);
        processProfile(mutation);
    };

    ownerTag.prototype.load = function () {};

    ownerTag.prototype.unload = function () {};

    ownerTag.prototype.stop = function () {
        // Remove tags that were added
        $(".kawaii-tag").remove();
        $(".kawaii-tagged").removeClass("kawaii-tagged");
    };

    ownerTag.prototype.getSettingsPanel = function () {
        return "";
    };

    ownerTag.prototype.getName = function () {
        return "Owner Tags";
    };

    ownerTag.prototype.getDescription = function () {
        return "Show a tag next to a server owner's name";
    };

    ownerTag.prototype.getVersion = function () {
        return "1.0.2";
    };

    ownerTag.prototype.getAuthor = function () {
        return "noodlebox";
    };
})();
<|endoftext|>