<commit_before>/**
 * @license
 * Copyright (C) 2013 Steven Berry (http://www.sberry.me/tablefilter)
 * Licensed: MIT (http://opensource.org/licenses/mit-license.php)
 * License Stipulations:
 *     1) Retain this comment block.
 *     2) Send me an email if you use this and have questions/comments!
 *
 * Steven Berry
 * www.sberry.me
 * steven@sberry.me
 */

(function(window, $, undefined) {
	var gid = 0;
	var idCache = [];
	var $css = $('');
	$('head').first().append($css);
	var prefix = 'tf_' + document.URL + '#';

	$.tablefilter = {
		debuffer: 350 // Default based on avg WPM and empirical testing in IE7
	};

	$.fn.tablefilter = function(findSelector, args) {
		var globalSave, result;
		var targetSelector = this.selector;

		if (typeof args === 'undefined') {
			args = findSelector;
			findSelector = 'tbody tr';
		}

		if (typeof args === 'undefined') {
			return this;
		}

		globalSave = (typeof args['save'] === 'boolean' && !!window.localStorage) ? args['save'] : false;

		result = this.filter('table').each(function(i, table) {
			var $noelements;

			table.tf = table.tf || {};
			if (typeof table.tf.noelements === 'undefined') {
				$noelements = $('
No items found.
');
				$(table).after($noelements);
				table.tf.noelements = $noelements[0];
			}
		});

		for (var selector in args['filters']) {
			$(selector).each(function(i, el) {
				var
					// References
					filter, defaultMethodKey,
					// Control Parameters
					event, checkColumns, reverse, save, applyClass, matchAttr,
					// Worker Functions
					filterMethod, getMatchAttr, onMatch, onNoMatch, saveMethod, getRemoveCondition,
					// Callback Functions
					action, callback, timeout;

				if (typeof el.tfid === 'undefined') {
					el.tfid = ++gid;
					idCache.push(el.tfid);
				}

				filter = args.filters[selector];

				event = filter.event;
				checkColumns = makeArray(filter.columns);
				reverse = getDefault(filter.reverse, typeof filter.apply === 'undefined');
				//save = getDefault(filter.save, globalSave);
				applyClass = filter.apply || ('tf-autogen-hide-' + el.tfid);
				matchAttr = filter.attribute || 'text';

				defaultMethodKey = (checkColumns.length > 0) ? 'cellMatch' : 'rowMatch';
				filterMethod = filterMethods[filter.method] || filterMethods[defaultMethodKey];

				if (typeof filter.match !== 'undefined') {
					getMatchAttr = function() {
						return filter.match;
					};
				}
				else {
					getMatchAttr = function() {
						return $(this).val();
					};
				}

				if (el.type === 'checkbox') {
					onMatch = addClass;
					onNoMatch = removeClass;
					getRemoveCondition = function() {
						return reverse ? this.checked : !this.checked;
					};
				}
				else {
					onMatch = reverse ? removeClass : addClass;
					onNoMatch = reverse ? addClass : removeClass;
					getRemoveCondition = function() {
						return $(this).val().length == 0;
					};
				}

				action = function(e) {
					var start = new Date();

					var $tableSet = $(targetSelector).filter('table');

					if (getRemoveCondition.call(this)) {
						$tableSet.find('tbody tr').removeClass(applyClass);
					}
					else {
						var value = getMatchAttr.call(this);
						filterMethod.call($tableSet, findSelector, value, matchAttr, checkColumns, applyClass, onMatch, onNoMatch);
					}

					//saveMethod.call(this);
					$tableSet.each(function(i, table) {
						if (table.tf.noelements !== 'undefined') {
							$(table.tf.noelements).css('display', $(table).find(findSelector).filter(':visible').length > 0 ? 'none' : 'block');
						}

						$(table).trigger('filterend');
					});

					console.log((new Date() - start) + 'ms');
				};

				if ($.inArray(event, ['keyup', 'keydown']) >= 0) {
					callback = function(e) {
						var that = this;
						clearTimeout(timeout);
						timeout = setTimeout(function() {
							action.call(that, e);
						}, $.tablefilter.debuffer);
					};
				}
				else {
					callback = action;
				}

				$(el).bind(event, callback);
				$(el).trigger(event);
			});
		}

		buildCss();

		return result;
	};

	$.fn.extend({
		tfmeta: function(name) {
			if (typeof name === 'undefined') {
				return this.text();
			}

			switch (name) {
				case 'text':
					return this.text();
				default:
					return this.attr(name);
			}
		}
	});

	var filterMethods = {
		search: function(findSelector, value, matchAttr, checkColumns, applyClass, onMatch, onNoMatch) {
			var text;
			var tokens = tokenize(value);

			$(this).find(findSelector).each(function(i, el) {
				(function() {
					for (var j = 0; j < tokens.length; j++) {
						text = $(el).tfmeta(matchAttr);
						if (!contains(text, tokens[j], 'i')) {
							onNoMatch.call(el, applyClass);
							return;
						}
					}
					onMatch.call(el, applyClass);
				})();
			});
		},

		rowMatch: function(findSelector, value, matchAttr, checkColumns, applyClass, onMatch, onNoMatch) {
			var text;

			$(this).find(findSelector).each(function(i, el) {
				text = $(el).tfmeta(matchAttr);
				if (equals(text, value)) {
					onMatch.call(el, applyClass);
				}
				else {
					onNoMatch.call(el, applyClass);
				}
			});
		},

		cellMatch: function(findSelector, value, matchAttr, checkColumns, applyClass, onMatch, onNoMatch) {
			var text, $cells;

			$(this).find(findSelector).each(function(i, el) {
				$cells = $(el).children('td');
				(function() {
					for (var j = 0; j < checkColumns.length; j++) {
						text = $cells.eq(checkColumns[j]).tfmeta(matchAttr);
						if (equals(text, value)) {
							onMatch.call(el, applyClass);
							return;
						}
					}
					onNoMatch.call(el, applyClass);
				})();
			});
		}
	};

	var
		getDefault = function(val, def) {
			return (typeof val === 'undefined') ? def : val;
		},

		buildCss = function() {
			var cssText = '';
			for (var i = 0; i < idCache.length; i++) {
				cssText += '.tf-autogen-hide-' + idCache[i] + '{display: none;} ';
			}

			try { $css.html($.trim(cssText)); }
			catch (ex) { $css[0].styleSheet.cssText = $.trim(cssText); }
		},

		tokenize = function(val) {
			var tokens = val.match(/\w+|"[^"]+"/g) || [''];
			for (var i = 0; i < tokens.length; i++) {
				tokens[i] = tokens[i].replace(/"/g, '');
			}

			return tokens;
		},

		makeArray = function(val) {
			if (typeof val === 'undefined') {
				return [];
			}

			return $.isArray(val) ? val : [val];
		},

		addClass = function(name) {
			$(this).addClass(name);
		},

		removeClass = function(name) {
			$(this).removeClass(name);
		},

		contains = function(val1, val2, flags) {
			if (typeof val1 === 'undefined' || typeof val2 === 'undefined') {
				return false;
			}

			val1 = val1.toString().toLowerCase();
			val2 = val2.toString().toLowerCase();

			return val1.indexOf(val2) >= 0;
		},

		equals = function(val1, val2, flags) {
			if (typeof val1 === 'undefined' || typeof val2 === 'undefined') {
				return false;
			}

			val1 = val1.toString().toLowerCase();
			val2 = val2.toString().toLowerCase();

			return val1 === val2;
		},

		storeValue = function() {
			if (typeof this.id !== 'undefined') {
				var key = prefix + this.id;

				try {
					window.localStorage[key] = (this.type === 'checkbox') ? this.checked : this.value;
				}
				catch(ex) { }
			}
		},

		loadValue = function() {
			if (typeof this.id !== 'undefined') {
				var key = prefix + this.id;

				if (typeof window.localStorage[key] !== 'undefined') {
					if (this.type === 'checkbox') {
						this.checked = window.localStorage[key] === 'true';
					}
					else {
						this.value = window.localStorage[key];
					}
				}
			}
		},

		loadState = function(selector) {
			$(selector).each(function(i, el) {
				loadValue.call(el);
			});
		};
})(window, jQuery);<commit_msg>Removing console debug statements.<commit_after>/**
 * @license
 * Copyright (C) 2013 Steven Berry (http://www.sberry.me/tablefilter)
 * Licensed: MIT (http://opensource.org/licenses/mit-license.php)
 * License Stipulations:
 *     1) Retain this comment block.
 *     2) Send me an email if you use this and have questions/comments!
 *
 * Steven Berry
 * www.sberry.me
 * steven@sberry.me
 */

(function(window, $, undefined) {
	var gid = 0;
	var idCache = [];
	var $css = $('');
	$('head').first().append($css);
	var prefix = 'tf_' + document.URL + '#';

	$.tablefilter = {
		debuffer: 350 // Default based on avg WPM and empirical testing in IE7
	};

	$.fn.tablefilter = function(findSelector, args) {
		var globalSave, result;
		var targetSelector = this.selector;

		if (typeof args === 'undefined') {
			args = findSelector;
			findSelector = 'tbody tr';
		}

		if (typeof args === 'undefined') {
			return this;
		}

		globalSave = (typeof args['save'] === 'boolean' && !!window.localStorage) ? args['save'] : false;

		result = this.filter('table').each(function(i, table) {
			var $noelements;

			table.tf = table.tf || {};
			if (typeof table.tf.noelements === 'undefined') {
				$noelements = $('
No items found.
');
				$(table).after($noelements);
				table.tf.noelements = $noelements[0];
			}
		});

		for (var selector in args['filters']) {
			$(selector).each(function(i, el) {
				var
					// References
					filter, defaultMethodKey,
					// Control Parameters
					event, checkColumns, reverse, save, applyClass, matchAttr,
					// Worker Functions
					filterMethod, getMatchAttr, onMatch, onNoMatch, saveMethod, getRemoveCondition,
					// Callback Functions
					action, callback, timeout;

				if (typeof el.tfid === 'undefined') {
					el.tfid = ++gid;
					idCache.push(el.tfid);
				}

				filter = args.filters[selector];

				event = filter.event;
				checkColumns = makeArray(filter.columns);
				reverse = getDefault(filter.reverse, typeof filter.apply === 'undefined');
				//save = getDefault(filter.save, globalSave);
				applyClass = filter.apply || ('tf-autogen-hide-' + el.tfid);
				matchAttr = filter.attribute || 'text';

				defaultMethodKey = (checkColumns.length > 0) ? 'cellMatch' : 'rowMatch';
				filterMethod = filterMethods[filter.method] || filterMethods[defaultMethodKey];

				if (typeof filter.match !== 'undefined') {
					getMatchAttr = function() {
						return filter.match;
					};
				}
				else {
					getMatchAttr = function() {
						return $(this).val();
					};
				}

				if (el.type === 'checkbox') {
					onMatch = addClass;
					onNoMatch = removeClass;
					getRemoveCondition = function() {
						return reverse ? this.checked : !this.checked;
					};
				}
				else {
					onMatch = reverse ? removeClass : addClass;
					onNoMatch = reverse ? addClass : removeClass;
					getRemoveCondition = function() {
						return $(this).val().length == 0;
					};
				}

				action = function(e) {
					//var start = new Date();

					var $tableSet = $(targetSelector).filter('table');

					if (getRemoveCondition.call(this)) {
						$tableSet.find('tbody tr').removeClass(applyClass);
					}
					else {
						var value = getMatchAttr.call(this);
						filterMethod.call($tableSet, findSelector, value, matchAttr, checkColumns, applyClass, onMatch, onNoMatch);
					}

					//saveMethod.call(this);
					$tableSet.each(function(i, table) {
						if (table.tf.noelements !== 'undefined') {
							$(table.tf.noelements).css('display', $(table).find(findSelector).filter(':visible').length > 0 ? 'none' : 'block');
						}

						$(table).trigger('filterend');
					});

					//console.log((new Date() - start) + 'ms');
				};

				if ($.inArray(event, ['keyup', 'keydown']) >= 0) {
					callback = function(e) {
						var that = this;
						clearTimeout(timeout);
						timeout = setTimeout(function() {
							action.call(that, e);
						}, $.tablefilter.debuffer);
					};
				}
				else {
					callback = action;
				}

				$(el).bind(event, callback);
				$(el).trigger(event);
			});
		}

		buildCss();

		return result;
	};

	$.fn.extend({
		tfmeta: function(name) {
			if (typeof name === 'undefined') {
				return this.text();
			}

			switch (name) {
				case 'text':
					return this.text();
				default:
					return this.attr(name);
			}
		}
	});

	var filterMethods = {
		search: function(findSelector, value, matchAttr, checkColumns, applyClass, onMatch, onNoMatch) {
			var text;
			var tokens = tokenize(value);

			$(this).find(findSelector).each(function(i, el) {
				(function() {
					for (var j = 0; j < tokens.length; j++) {
						text = $(el).tfmeta(matchAttr);
						if (!contains(text, tokens[j], 'i')) {
							onNoMatch.call(el, applyClass);
							return;
						}
					}
					onMatch.call(el, applyClass);
				})();
			});
		},

		rowMatch: function(findSelector, value, matchAttr, checkColumns, applyClass, onMatch, onNoMatch) {
			var text;

			$(this).find(findSelector).each(function(i, el) {
				text = $(el).tfmeta(matchAttr);
				if (equals(text, value)) {
					onMatch.call(el, applyClass);
				}
				else {
					onNoMatch.call(el, applyClass);
				}
			});
		},

		cellMatch: function(findSelector, value, matchAttr, checkColumns, applyClass, onMatch, onNoMatch) {
			var text, $cells;

			$(this).find(findSelector).each(function(i, el) {
				$cells = $(el).children('td');
				(function() {
					for (var j = 0; j < checkColumns.length; j++) {
						text = $cells.eq(checkColumns[j]).tfmeta(matchAttr);
						if (equals(text, value)) {
							onMatch.call(el, applyClass);
							return;
						}
					}
					onNoMatch.call(el, applyClass);
				})();
			});
		}
	};

	var
		getDefault = function(val, def) {
			return (typeof val === 'undefined') ? def : val;
		},

		buildCss = function() {
			var cssText = '';
			for (var i = 0; i < idCache.length; i++) {
				cssText += '.tf-autogen-hide-' + idCache[i] + '{display: none;} ';
			}

			try { $css.html($.trim(cssText)); }
			catch (ex) { $css[0].styleSheet.cssText = $.trim(cssText); }
		},

		tokenize = function(val) {
			var tokens = val.match(/\w+|"[^"]+"/g) || [''];
			for (var i = 0; i < tokens.length; i++) {
				tokens[i] = tokens[i].replace(/"/g, '');
			}

			return tokens;
		},

		makeArray = function(val) {
			if (typeof val === 'undefined') {
				return [];
			}

			return $.isArray(val) ? val : [val];
		},

		addClass = function(name) {
			$(this).addClass(name);
		},

		removeClass = function(name) {
			$(this).removeClass(name);
		},

		contains = function(val1, val2, flags) {
			if (typeof val1 === 'undefined' || typeof val2 === 'undefined') {
				return false;
			}

			val1 = val1.toString().toLowerCase();
			val2 = val2.toString().toLowerCase();

			return val1.indexOf(val2) >= 0;
		},

		equals = function(val1, val2, flags) {
			if (typeof val1 === 'undefined' || typeof val2 === 'undefined') {
				return false;
			}

			val1 = val1.toString().toLowerCase();
			val2 = val2.toString().toLowerCase();

			return val1 === val2;
		},

		storeValue = function() {
			if (typeof this.id !== 'undefined') {
				var key = prefix + this.id;

				try {
					window.localStorage[key] = (this.type === 'checkbox') ? this.checked : this.value;
				}
				catch(ex) { }
			}
		},

		loadValue = function() {
			if (typeof this.id !== 'undefined') {
				var key = prefix + this.id;

				if (typeof window.localStorage[key] !== 'undefined') {
					if (this.type === 'checkbox') {
						this.checked = window.localStorage[key] === 'true';
					}
					else {
						this.value = window.localStorage[key];
					}
				}
			}
		},

		loadState = function(selector) {
			$(selector).each(function(i, el) {
				loadValue.call(el);
			});
		};
})(window, jQuery);<|endoftext|>


<commit_before>let webpack = require('webpack');
let mix = require('laravel-mix');
let fs = require('fs');
let path = require('path');
let exec = require('child_process').exec;
let package = JSON.parse(fs.readFileSync('./package.json'));
let CopyWebpackPlugin = require('copy-webpack-plugin');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for your application, as well as bundling up your JS files.
 |
 */

// Copy the WSU HTTP Error templates, css, images
mix.copy([
    'vendor/waynestate/error-404/dist/404.css',
    'vendor/waynestate/error-404/dist/404.css.map',
    'vendor/waynestate/error-403/dist/403.css',
    'vendor/waynestate/error-403/dist/403.css.map',
    'vendor/waynestate/error-500/dist/500.css',
    'vendor/waynestate/error-500/dist/500.css.map'
], 'public/_resources/css');
mix.copy([
    'vendor/waynestate/error-404/dist/404.png',
    'vendor/waynestate/error-403/dist/403.png',
    'vendor/waynestate/error-500/dist/500.png'
], 'public/_resources/images');

// Copy the fonts
mix.copy([
    'resources/fonts/**/*',
    'node_modules/slick-carousel/slick/fonts/**/*'
], 'public/_resources/fonts');

// Copy the images
mix.copy([
    'resources/images/**/*.jpg',
    'resources/images/**/*.gif',
    'resources/images/**/*.png',
    'resources/images/**/*.svg',
    'node_modules/slick-carousel/slick/**/*.gif'
], 'public/_resources/images');

// Create the _static symlink
fs.symlink(
    path.resolve('./storage/app/public'),
    path.resolve('./public/_static'),
    function (err) { err.errno != -17 ? console.log(err) : console.log("Done."); }
);

// Compile JS and SCSS
mix.js('resources/js/main.js', 'public/_resources/js')
   .sass('resources/scss/main.scss', 'public/_resources/css');

// We
mix.options({
    processCssUrls: false
});

// Auto refresh web browser when changes take place
mix.browserSync({
    proxy: 'https://' + package.name + '.wayne.local'
});

if (mix.inProduction()) {
    mix.version(['public/_resources/css/main.css', 'public/_resources/js/main.js']);
}

// Override webpack configuration
mix.webpackConfig({
    module: {
        rules: [{
            test: /\.js$/,
            exclude: /node_modules/,
            enforce: 'pre',
            use: [{
                loader: 'eslint-loader'
            }],
        }],
    },
    plugins: [
        new webpack.LoaderOptionsPlugin({
            options: {
                eslint: {
                    configFile: path.join(__dirname, '.eslintrc'),
                },
            },
        }),
        new CopyWebpackPlugin([
            {
                from: 'node_modules/@waynestate/wsuheader/dist/header.html',
                to: path.resolve('resources/views/partials/header.blade.php'),
            },
            {
                from: 'node_modules/@waynestate/wsufooter/dist/footer.html',
                to: path.resolve('resources/views/partials/footer.blade.php'),
            },
            {
                from: 'vendor/waynestate/error-404/dist/404.php',
                to: path.resolve('resources/views/errors/404.blade.php'),
            },
            {
                from: 'vendor/waynestate/error-403/dist/403.php',
                to: path.resolve('resources/views/errors/403.blade.php'),
            },
            {
                from: 'vendor/waynestate/error-500/dist/500.php',
                to: path.resolve('resources/views/errors/500.blade.php'),
            },
            {
                from: 'hooks',
                to: path.resolve('.git/hooks'),
            }
        ]),
    ]
});

// Full API
// mix.js(src, output);
// mix.react(src, output); <-- Identical to mix.js(), but registers React Babel compilation.
// mix.ts(src, output); <-- Requires tsconfig.json to exist in the same folder as webpack.mix.js
// mix.extract(vendorLibs);
// mix.sass(src, output);
// mix.standaloneSass('src', output); <-- Faster, but isolated from Webpack.
// mix.fastSass('src', output); <-- Alias for mix.standaloneSass().
// mix.less(src, output);
// mix.stylus(src, output);
// mix.postCss(src, output, [require('postcss-some-plugin')()]);
// mix.browserSync('my-site.dev');
// mix.combine(files, destination);
// mix.babel(files, destination); <-- Identical to mix.combine(), but also includes Babel compilation.
// mix.copy(from, to);
// mix.copyDirectory(fromDir, toDir);
// mix.minify(file);
// mix.sourceMaps(); // Enable sourcemaps
// mix.version(); // Enable versioning.
// mix.disableNotifications();
// mix.setPublicPath('path/to/public');
// mix.setResourceRoot('prefix/for/resource/locators');
// mix.autoload({}); <-- Will be passed to Webpack's ProvidePlugin.
// mix.webpackConfig({}); <-- Override webpack.config.js, without editing the file directly.
// mix.then(function () {}) <-- Will be triggered each time Webpack finishes building.
// mix.options({
//   extractVueStyles: false, // Extract .vue component styling to file, rather than inline.
//   globalVueStyles: file, // Variables file to be imported in every component.
//   processCssUrls: true, // Process/optimize relative stylesheet url()'s. Set to false, if you don't want them touched.
//   purifyCss: false, // Remove unused CSS selectors.
//   uglify: {}, // Uglify-specific options. https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
//   postCss: [] // Post-CSS options: https://github.com/postcss/postcss/blob/master/docs/plugins.md
// });<commit_msg>version all assets as needeed<commit_after>let webpack = require('webpack');
let mix = require('laravel-mix');
let fs = require('fs');
let path = require('path');
let exec = require('child_process').exec;
let package = JSON.parse(fs.readFileSync('./package.json'));
let CopyWebpackPlugin = require('copy-webpack-plugin');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for your application, as well as bundling up your JS files.
 |
 */

// Copy the WSU HTTP Error templates, css, images
mix.copy([
    'vendor/waynestate/error-404/dist/404.css',
    'vendor/waynestate/error-404/dist/404.css.map',
    'vendor/waynestate/error-403/dist/403.css',
    'vendor/waynestate/error-403/dist/403.css.map',
    'vendor/waynestate/error-500/dist/500.css',
    'vendor/waynestate/error-500/dist/500.css.map'
], 'public/_resources/css');
mix.copy([
    'vendor/waynestate/error-404/dist/404.png',
    'vendor/waynestate/error-403/dist/403.png',
    'vendor/waynestate/error-500/dist/500.png'
], 'public/_resources/images');

// Copy the fonts
mix.copy([
    'resources/fonts/**/*',
    'node_modules/slick-carousel/slick/fonts/**/*'
], 'public/_resources/fonts');

// Copy the images
mix.copy([
    'resources/images/**/*.jpg',
    'resources/images/**/*.gif',
    'resources/images/**/*.png',
    'resources/images/**/*.svg',
    'node_modules/slick-carousel/slick/**/*.gif'
], 'public/_resources/images');

// Create the _static symlink
fs.symlink(
    path.resolve('./storage/app/public'),
    path.resolve('./public/_static'),
    function (err) { err.errno != -17 ? console.log(err) : console.log("Done."); }
);

// Compile JS and SCSS
mix.js('resources/js/main.js', 'public/_resources/js')
   .sass('resources/scss/main.scss', 'public/_resources/css');

// We
mix.options({
    processCssUrls: false
});

// Auto refresh web browser when changes take place
mix.browserSync({
    proxy: 'https://' + package.name + '.wayne.local'
});

if (mix.inProduction()) {
    mix.version();
}

// Override webpack configuration
mix.webpackConfig({
    module: {
        rules: [{
            test: /\.js$/,
            exclude: /node_modules/,
            enforce: 'pre',
            use: [{
                loader: 'eslint-loader'
            }],
        }],
    },
    plugins: [
        new webpack.LoaderOptionsPlugin({
            options: {
                eslint: {
                    configFile: path.join(__dirname, '.eslintrc'),
                },
            },
        }),
        new CopyWebpackPlugin([
            {
                from: 'node_modules/@waynestate/wsuheader/dist/header.html',
                to: path.resolve('resources/views/partials/header.blade.php'),
            },
            {
                from: 'node_modules/@waynestate/wsufooter/dist/footer.html',
                to: path.resolve('resources/views/partials/footer.blade.php'),
            },
            {
                from: 'vendor/waynestate/error-404/dist/404.php',
                to: path.resolve('resources/views/errors/404.blade.php'),
            },
            {
                from: 'vendor/waynestate/error-403/dist/403.php',
                to: path.resolve('resources/views/errors/403.blade.php'),
            },
            {
                from: 'vendor/waynestate/error-500/dist/500.php',
                to: path.resolve('resources/views/errors/500.blade.php'),
            },
            {
                from: 'hooks',
                to: path.resolve('.git/hooks'),
            }
        ]),
    ]
});

// Full API
// mix.js(src, output);
// mix.react(src, output); <-- Identical to mix.js(), but registers React Babel compilation.
// mix.ts(src, output); <-- Requires tsconfig.json to exist in the same folder as webpack.mix.js
// mix.extract(vendorLibs);
// mix.sass(src, output);
// mix.standaloneSass('src', output); <-- Faster, but isolated from Webpack.
// mix.fastSass('src', output); <-- Alias for mix.standaloneSass().
// mix.less(src, output);
// mix.stylus(src, output);
// mix.postCss(src, output, [require('postcss-some-plugin')()]);
// mix.browserSync('my-site.dev');
// mix.combine(files, destination);
// mix.babel(files, destination); <-- Identical to mix.combine(), but also includes Babel compilation.
// mix.copy(from, to);
// mix.copyDirectory(fromDir, toDir);
// mix.minify(file);
// mix.sourceMaps(); // Enable sourcemaps
// mix.version(); // Enable versioning.
// mix.disableNotifications();
// mix.setPublicPath('path/to/public');
// mix.setResourceRoot('prefix/for/resource/locators');
// mix.autoload({}); <-- Will be passed to Webpack's ProvidePlugin.
// mix.webpackConfig({}); <-- Override webpack.config.js, without editing the file directly.
// mix.then(function () {}) <-- Will be triggered each time Webpack finishes building.
// mix.options({
//   extractVueStyles: false, // Extract .vue component styling to file, rather than inline.
//   globalVueStyles: file, // Variables file to be imported in every component.
//   processCssUrls: true, // Process/optimize relative stylesheet url()'s. Set to false, if you don't want them touched.
//   purifyCss: false, // Remove unused CSS selectors.
//   uglify: {}, // Uglify-specific options. https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
//   postCss: [] // Post-CSS options: https://github.com/postcss/postcss/blob/master/docs/plugins.md
// });<|endoftext|>


<commit_before>'use strict'

import React from 'react'
import { hot, setConfig } from 'react-hot-loader'
import { Redirect } from 'react-router-dom'
import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next'
import { useObserver } from 'mobx-react'
import classNames from 'classnames'

import RootContext from '../context/RootContext'

import BackgroundAnimation from '../components/BackgroundAnimation'
import JoinChannel from '../components/JoinChannel'
import Spinner from '../components/Spinner'

import ChannelLink from './ChannelLink'

import '../styles/flaticon.css'
import '../styles/ControlPanel.scss'

setConfig({
  pureSFC: true,
  pureRender: true
})

function ControlPanel ({ history }) {
  const { networkStore, uiStore, sessionStore } = React.useContext(RootContext)
  const [t] = useTranslation()

  const [redirect, setRedirect] = React.useState('')

  const inputRef = React.useRef()

  const focusInput = React.useCallback(() => {
    if (inputRef.current) inputRef.current.focus()
  }, [])

  const isClosable = history.location ? history.location.pathname !== '/' : true

  const handleClose = React.useCallback(
    force => {
      if (force || isClosable) uiStore.closeControlPanel()
    },
    [isClosable]
  )

  const handleChannelLinkClick = React.useCallback((e, channel) => {
    e.preventDefault()
    setRedirect(`/channel/${channel.channelName}`)
  }, [])

  const handleJoinChannel = React.useCallback(e => {
    e.preventDefault()
    if (!inputRef.current) return
    const channel = inputRef.current.value.trim()
    networkStore.joinChannel(channel).then(() => {
      inputRef.current.value = ''
      setRedirect(`/channel/${channel}`)
    })
  }, [])

  const handleCloseChannel = React.useCallback(
    channel => {
      if (uiStore.currentChannelName === channel.channelName) setRedirect('/')
      networkStore.leaveChannel(channel.channelName)
    },
    [uiStore.currentChannelName]
  )

  React.useLayoutEffect(focusInput)
  React.useLayoutEffect(() => {
    if (redirect) handleClose(redirect !== '/')
  }, [handleClose, redirect])

  function renderJoinChannelInput () {
    return networkStore.isOnline ? (

        <JoinChannel
          onSubmit={handleJoinChannel}
          autoFocus
          theme={{ ...uiStore.theme }}
          inputRef={inputRef}
        />

    ) : !networkStore.starting ? (
      <button
        className='startIpfsButton submitButton'
        style={{ ...uiStore.theme }}
        onClick={() => networkStore.start()}
      >
        {t('controlPanel.startJsIpfs')}
      </button>
    ) : (

        <Spinner />

    )
  }

  function renderChannelsList () {
    return (
      <>

 0,
            hidden: networkStore.channelsAsArray.length === 0
          })}
        >
          {t('controlPanel.channels')}

            {networkStore.channelsAsArray.map(c => (

                <ChannelLink
                  channel={c}
                  theme={{ ...uiStore.theme }}
                  onClick={e => handleChannelLinkClick(e, c)}
                />
                 handleCloseChannel(c)}>
                  {t('controlPanel.closeChannel')}

            ))}

      </>
    )
  }

  function renderBottomRow () {
    return (

 setRedirect('/settings')}
          style={{ ...uiStore.theme }}
          key='settingsIcon'
        />
        {/*
 */}

 sessionStore.logout()}
          style={{ ...uiStore.theme }}
          key='disconnectIcon'
        />

    )
  }

  return useObserver(() =>
    uiStore.isControlPanelOpen && sessionStore.isAuthenticated ? (
      <>

            <BackgroundAnimation
              size={320}
              startY={58}
              theme={{ ...uiStore.theme }}
              style={{ alignItems: 'flex-start' }}
            />

Orbit

            {networkStore.networkName}

{sessionStore.username}

          {renderJoinChannelInput()}
          {renderChannelsList()}
          {renderBottomRow()}

        {redirect ? <Redirect to={redirect} /> : null}
      </>
    ) : null
  )
}

ControlPanel.propTypes = {
  history: PropTypes.object.isRequired
}

export default hot(module)(ControlPanel)
<commit_msg>Fix redirecting back to last channel when ControlPanel is opened<commit_after>'use strict'

import React from 'react'
import { hot, setConfig } from 'react-hot-loader'
import { Redirect } from 'react-router-dom'
import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next'
import { useObserver } from 'mobx-react'
import classNames from 'classnames'

import RootContext from '../context/RootContext'

import BackgroundAnimation from '../components/BackgroundAnimation'
import JoinChannel from '../components/JoinChannel'
import Spinner from '../components/Spinner'

import ChannelLink from './ChannelLink'

import '../styles/flaticon.css'
import '../styles/ControlPanel.scss'

setConfig({
  pureSFC: true,
  pureRender: true
})

function ControlPanel ({ history }) {
  const { networkStore, uiStore, sessionStore } = React.useContext(RootContext)
  const [t] = useTranslation()

  const [redirect, setRedirect] = React.useState('')

  const inputRef = React.useRef()

  const focusInput = React.useCallback(() => {
    if (inputRef.current) inputRef.current.focus()
  }, [])

  const isClosable = history.location ? history.location.pathname !== '/' : true

  const handleClosePanel = React.useCallback(
    force => {
      if (force || isClosable) uiStore.closeControlPanel()
    },
    [isClosable]
  )

  const handleChannelLinkClick = React.useCallback((e, channel) => {
    e.preventDefault()
    setRedirect(`/channel/${channel.channelName}`)
  }, [])

  const handleJoinChannel = React.useCallback(e => {
    e.preventDefault()
    if (!inputRef.current) return
    const channel = inputRef.current.value.trim()
    networkStore.joinChannel(channel).then(() => {
      inputRef.current.value = ''
      setRedirect(`/channel/${channel}`)
    })
  }, [])

  const handleCloseChannel = React.useCallback(
    channel => {
      if (uiStore.currentChannelName === channel.channelName) setRedirect('/')
      networkStore.leaveChannel(channel.channelName)
    },
    [uiStore.currentChannelName]
  )

  React.useLayoutEffect(() => {
    if (uiStore.isControlPanelOpen) focusInput()
  }, [focusInput, uiStore.isControlPanelOpen])

  React.useEffect(() => {
    if (redirect) {
      handleClosePanel(redirect !== '/')
      setRedirect('')
    }
  }, [handleClosePanel, redirect])

  function renderJoinChannelInput () {
    return networkStore.isOnline ? (

        <JoinChannel
          onSubmit={handleJoinChannel}
          autoFocus
          theme={{ ...uiStore.theme }}
          inputRef={inputRef}
        />

    ) : !networkStore.starting ? (
      <button
        className='startIpfsButton submitButton'
        style={{ ...uiStore.theme }}
        onClick={() => networkStore.start()}
      >
        {t('controlPanel.startJsIpfs')}
      </button>
    ) : (

        <Spinner />

    )
  }

  function renderChannelsList () {
    return (
      <>

 0,
            hidden: networkStore.channelsAsArray.length === 0
          })}
        >
          {t('controlPanel.channels')}

            {networkStore.channelsAsArray.map(c => (

                <ChannelLink
                  channel={c}
                  theme={{ ...uiStore.theme }}
                  onClick={e => handleChannelLinkClick(e, c)}
                />
                 handleCloseChannel(c)}>
                  {t('controlPanel.closeChannel')}

            ))}

      </>
    )
  }

  function renderBottomRow () {
    return (

 setRedirect('/settings')}
          style={{ ...uiStore.theme }}
          key='settingsIcon'
        />
        {/*
 */}

 sessionStore.logout()}
          style={{ ...uiStore.theme }}
          key='disconnectIcon'
        />

    )
  }

  return useObserver(() =>
    uiStore.isControlPanelOpen && sessionStore.isAuthenticated ? (
      <>

            <BackgroundAnimation
              size={320}
              startY={58}
              theme={{ ...uiStore.theme }}
              style={{ alignItems: 'flex-start' }}
            />

Orbit

            {networkStore.networkName}

{sessionStore.username}

          {renderJoinChannelInput()}
          {renderChannelsList()}
          {renderBottomRow()}

        {redirect ? <Redirect to={redirect} /> : null}
      </>
    ) : null
  )
}

ControlPanel.propTypes = {
  history: PropTypes.object.isRequired
}

export default hot(module)(ControlPanel)
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Breadcrumbs from '../components/Breadcrumbs';

class MapLocations extends Component {
	render(){
		return(

					<Breadcrumbs/>

				What part of the island do you want to be trained at?

					MAP GOES HERE

		);
	}
}

export default MapLocations;<commit_msg>added button<commit_after>import React, { Component } from 'react';
import Breadcrumbs from '../components/Breadcrumbs';

class MapLocations extends Component {
	render(){
		return(

					<Breadcrumbs/>

				What part of the island do you want to be trained at?

					MAP GOES HERE

						District:

						Place

						Name:
						Address:
						Times:

					<button>Sign Up</button>

		);
	}
}

export default MapLocations;<|endoftext|>


<commit_before>import React, { Component } from 'react'
import {
  StyleSheet,
  TouchableOpacity,
  ImageBackground,
  Image
} from 'react-native'
import PropTypes from 'prop-types'
import { ifIphoneX } from 'react-native-iphone-x-helper'

import { View } from 'react-native-animatable'

import TextPingFang from '../../components/TextPingFang'

import {
  WIDTH,
  getResponsiveHeight,
  getResponsiveWidth
} from '../../common/styles'

export default class Banner extends Component {
  static propTypes = {
    bg: PropTypes.number.isRequired,
    title: PropTypes.arrayOf(PropTypes.string).isRequired,
    showNavLeft: PropTypes.bool.isRequired,
    showNavRight: PropTypes.bool.isRequired,
    onNavLeftPress: PropTypes.func,
    onNavRightPress: PropTypes.func
  }

  render() {
    return (
      <View style={styles.container} animation='fadeIn'>
        <ImageBackground style={styles.banner} source={this.props.bg}>
          <View style={styles.nav_bar}>
            <TouchableOpacity style={styles.nav_left_container} onPress={this.props.onNavLeftPress}>
              <Image
                style={{ display: this.props.showNavLeft ? 'flex' : 'none' }}
                source={require('../../../res/images/common/icon_back_white.png')}/>
            </TouchableOpacity>
            <TouchableOpacity style={{display: Date.now() > new Date('2018-09-12').getTime() ? 'flex' : 'none'}} onPress={this.props.onNavRightPress}>
              <TextPingFang style={[styles.text_nav_right, { display: this.props.showNavRight ? 'flex' : 'none' }]}>直接使用</TextPingFang>
            </TouchableOpacity>
          </View>
          <View style={styles.title_container}>
            <TextPingFang style={styles.text_title}>{this.props.title[0]}</TextPingFang>
            <TextPingFang style={styles.text_title}>{this.props.title[1]}</TextPingFang>
          </View>
        </ImageBackground>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {},
  banner: {
    width: WIDTH,
    height: getResponsiveHeight(224),
    ...ifIphoneX({
      paddingTop: 44
    }, {
      paddingTop: 20
    })
  },
  nav_bar: {
    width: WIDTH,
    height: 44,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    paddingLeft: getResponsiveWidth(18),
    paddingRight: getResponsiveWidth(18)
  },
  nav_left_container: {
    width: getResponsiveWidth(25)
  },
  text_nav_right: {
    color: '#000',
    fontSize: 16,
    fontWeight: '300',
  },
  title_container: {
    marginLeft: getResponsiveWidth(72),
    marginTop: getResponsiveHeight(18)
  },
  text_title: {
    color: '#000',
    fontSize: 34,
    fontWeight: 'bold',
  }
})
<commit_msg>object: （圣 圣诞节前屏蔽直接登录按钮<commit_after>import React, { Component } from 'react'
import {
  StyleSheet,
  TouchableOpacity,
  ImageBackground,
  Image
} from 'react-native'
import PropTypes from 'prop-types'
import { ifIphoneX } from 'react-native-iphone-x-helper'

import { View } from 'react-native-animatable'

import TextPingFang from '../../components/TextPingFang'

import {
  WIDTH,
  getResponsiveHeight,
  getResponsiveWidth
} from '../../common/styles'

export default class Banner extends Component {
  static propTypes = {
    bg: PropTypes.number.isRequired,
    title: PropTypes.arrayOf(PropTypes.string).isRequired,
    showNavLeft: PropTypes.bool.isRequired,
    showNavRight: PropTypes.bool.isRequired,
    onNavLeftPress: PropTypes.func,
    onNavRightPress: PropTypes.func
  }

  render() {
    return (
      <View style={styles.container} animation='fadeIn'>
        <ImageBackground style={styles.banner} source={this.props.bg}>
          <View style={styles.nav_bar}>
            <TouchableOpacity style={styles.nav_left_container} onPress={this.props.onNavLeftPress}>
              <Image
                style={{ display: this.props.showNavLeft ? 'flex' : 'none' }}
                source={require('../../../res/images/common/icon_back_white.png')}/>
            </TouchableOpacity>
            <TouchableOpacity style={{display: Date.now() > new Date('2018-12-25').getTime() ? 'flex' : 'none'}} onPress={this.props.onNavRightPress}>
              <TextPingFang style={[styles.text_nav_right, { display: this.props.showNavRight ? 'flex' : 'none' }]}>直接使用</TextPingFang>
            </TouchableOpacity>
          </View>
          <View style={styles.title_container}>
            <TextPingFang style={styles.text_title}>{this.props.title[0]}</TextPingFang>
            <TextPingFang style={styles.text_title}>{this.props.title[1]}</TextPingFang>
          </View>
        </ImageBackground>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {},
  banner: {
    width: WIDTH,
    height: getResponsiveHeight(224),
    ...ifIphoneX({
      paddingTop: 44
    }, {
      paddingTop: 20
    })
  },
  nav_bar: {
    width: WIDTH,
    height: 44,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    paddingLeft: getResponsiveWidth(18),
    paddingRight: getResponsiveWidth(18)
  },
  nav_left_container: {
    width: getResponsiveWidth(25)
  },
  text_nav_right: {
    color: '#000',
    fontSize: 16,
    fontWeight: '300',
  },
  title_container: {
    marginLeft: getResponsiveWidth(72),
    marginTop: getResponsiveHeight(18)
  },
  text_title: {
    color: '#000',
    fontSize: 34,
    fontWeight: 'bold',
  }
})
<|endoftext|>


<commit_before>// Exposed various uri schemes for other applications to query data from Cerveau
// Basically http responses that are not HTML, probably JSON

var app = require("./app");

module.exports = function(args) {
    var lobby = args.lobby;

    app.get('/status/:gameName/:gameSession', function(req, res) {
        var response = {};
        if(req.params.gameName && req.params.gameSession) {
            var info = lobby.getGameSessionInfo(req.params.gameName, req.params.gameSession);
            if(!info.error) {
                response = info;
                response.status = (info.over === true ? "over" : "running");
                response.gamelog = undefined;
            }
            else {
                if(info.sessionID !== undefined) {
                    response.status = "open";
                }
                else {
                    response.status = "error";
                    response.error = info.error;
                    response.gameName = info.gameName;
                }
            }
        }
        else {
            response.status = "error";
            response.error = "gameName or gameSession not sent!";
        }

        res.json(response);
    });

    app.get('/gamelog/:gameName/:gameSession/:requestedEpoch?', function(req, res) {
        var response = {}
        if(!req.params.gameName || !req.params.gameSession) {
            response.error = "gameName or gameSession not sent!";
        }
        else {
            req.params.requestedEpoch = req.params.requestedEpoch || 0;
            var gamelog = lobby.gameLogger.getLog(req.params.gameName, req.params.gameSession, req.params.requestedEpoch);

            if(gamelog) {
                response = gamelog;
            }
            else {
                response.error ="gamelog not found";
            }
        }

        res.json(response);
    });

    app.get('/visualize/:gameName/:gameSession/:requestedEpoch?', function(req, res) {
        res.render('visualize', req.params);
    });
};
<commit_msg>corss origin fun times<commit_after>// Exposed various uri schemes for other applications to query data from Cerveau
// Basically http responses that are not HTML, probably JSON

var app = require("./app");

module.exports = function(args) {
    var lobby = args.lobby;

    app.get('/status/:gameName/:gameSession', function(req, res) {
        var response = {};
        if(req.params.gameName && req.params.gameSession) {
            var info = lobby.getGameSessionInfo(req.params.gameName, req.params.gameSession);
            if(!info.error) {
                response = info;
                response.status = (info.over === true ? "over" : "running");
                response.gamelog = undefined;
            }
            else {
                if(info.sessionID !== undefined) {
                    response.status = "open";
                }
                else {
                    response.status = "error";
                    response.error = info.error;
                    response.gameName = info.gameName;
                }
            }
        }
        else {
            response.status = "error";
            response.error = "gameName or gameSession not sent!";
        }

        res.json(response);
    });

    app.get('/gamelog/:gameName/:gameSession/:requestedEpoch?', function(req, res) {
        var response = {}
        if(!req.params.gameName || !req.params.gameSession) {
            response.error = "gameName or gameSession not sent!";
        }
        else {
            req.params.requestedEpoch = req.params.requestedEpoch || 0;
            var gamelog = lobby.gameLogger.getLog(req.params.gameName, req.params.gameSession, req.params.requestedEpoch);

            if(gamelog) {
                response = gamelog;
            }
            else {
                response.error ="gamelog not found";
            }
        }

        res.header("Access-Control-Allow-Origin", "*");
        res.header("Access-Control-Allow-Headers", "X-Requested-With");

        res.json(response);
    });

    app.get('/visualize/:gameName/:gameSession/:requestedEpoch?', function(req, res) {
        res.render('visualize', req.params);
    });
};
<|endoftext|>


<commit_before>'use strict';

/**
 * Binds a ACE Editor widget
 */
angular.module('ui.ace', [])
  .constant('uiAceConfig', {})
  .directive('uiAce', ['uiAceConfig', function (uiAceConfig) {

    if (angular.isUndefined(window.ace)) {
      throw new Error('ui-ace need ace to work... (o rly?)');
    }

    /**
     * Sets editor options such as the wrapping mode or the syntax checker.
     *
     * The supported options are:
     *
     *

     *
showGutter

     *
useWrapMode

     *
onLoad

     *
theme

     *
mode

     *

     *
     * @param acee
     * @param session ACE editor session
     * @param {object} opts Options to be set
     */
    var setOptions = function(acee, session, opts) {

      // sets the ace worker path, if running from concatenated
      // or minified source
      if (angular.isDefined(opts.workerPath)) {
        var config = window.ace.require('ace/config');
        config.set('workerPath', opts.workerPath);
      }
      // ace requires loading
      if (angular.isDefined(opts.require)) {
        opts.require.forEach(function (n) {
            window.ace.require(n);
        });
      }
      // Boolean options
      if (angular.isDefined(opts.showGutter)) {
        acee.renderer.setShowGutter(opts.showGutter);
      }
      if (angular.isDefined(opts.useWrapMode)) {
        session.setUseWrapMode(opts.useWrapMode);
      }
      if (angular.isDefined(opts.showInvisibles)) {
        acee.renderer.setShowInvisibles(opts.showInvisibles);
      }
      if (angular.isDefined(opts.showIndentGuides)) {
        acee.renderer.setDisplayIndentGuides(opts.showIndentGuides);
      }
      if (angular.isDefined(opts.useSoftTabs)) {
        session.setUseSoftTabs(opts.useSoftTabs);
      }
      if (angular.isDefined(opts.showPrintMargin)) {
        acee.setShowPrintMargin(opts.showPrintMargin);
      }

      // commands
      if (angular.isDefined(opts.disableSearch) && opts.disableSearch) {
        acee.commands.addCommands([
          {
            name: 'unfind',
            bindKey: {
              win: 'Ctrl-F',
              mac: 'Command-F'
            },
            exec: function () {
              return false;
            },
            readOnly: true
          }
        ]);
      }

      // Basic options
      if (angular.isString(opts.theme)) {
        acee.setTheme('ace/theme/' + opts.theme);
      }
      if (angular.isString(opts.mode)) {
        session.setMode('ace/mode/' + opts.mode);
      }
      // Advanced options
      if (angular.isDefined(opts.firstLineNumber)) {
        if (angular.isNumber(opts.firstLineNumber)) {
          session.setOption('firstLineNumber', opts.firstLineNumber);
        } else if (angular.isFunction(opts.firstLineNumber)) {
          session.setOption('firstLineNumber', opts.firstLineNumber());
        }
      }

      // advanced options
      var key, obj;
      if (angular.isDefined(opts.advanced)) {
          for (key in opts.advanced) {
              // create a javascript object with the key and value
              obj = { name: key, value: opts.advanced[key] };
              // try to assign the option to the ace editor
              acee.setOption(obj.name, obj.value);
          }
      }

      // advanced options for the renderer
      if (angular.isDefined(opts.rendererOptions)) {
          for (key in opts.rendererOptions) {
              // create a javascript object with the key and value
              obj = { name: key, value: opts.rendererOptions[key] };
              // try to assign the option to the ace editor
              acee.renderer.setOption(obj.name, obj.value);
          }
      }

      // onLoad callbacks
      angular.forEach(opts.callbacks, function (cb) {
        if (angular.isFunction(cb)) {
          cb(acee);
        }
      });
    };

    return {
      restrict: 'EA',
      require: '?ngModel',
      link: function (scope, elm, attrs, ngModel) {

        /**
         * Corresponds the uiAceConfig ACE configuration.
         * @type object
         */
        var options = uiAceConfig.ace || {};

        /**
         * uiAceConfig merged with user options via json in attribute or data binding
         * @type object
         */
        var opts = angular.extend({}, options, scope.$eval(attrs.uiAce));

        /**
         * ACE editor
         * @type object
         */
        var acee = window.ace.edit(elm[0]);

        /**
         * ACE editor session.
         * @type object
         * @see [EditSession]{@link http://ace.c9.io/#nav=api&api=edit_session}
         */
        var session = acee.getSession();

        /**
         * Reference to a change listener created by the listener factory.
         * @function
         * @see listenerFactory.onChange
         */
        var onChangeListener;

        /**
         * Reference to a blur listener created by the listener factory.
         * @function
         * @see listenerFactory.onBlur
         */
        var onBlurListener;

        /**
         * Calls a callback by checking its existing. The argument list
         * is variable and thus this function is relying on the arguments
         * object.
         * @throws {Error} If the callback isn't a function
         */
        var executeUserCallback = function () {

          /**
           * The callback function grabbed from the array-like arguments
           * object. The first argument should always be the callback.
           *
           * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments}
           * @type {*}
           */
          var callback = arguments[0];

          /**
           * Arguments to be passed to the callback. These are taken
           * from the array-like arguments object. The first argument
           * is stripped because that should be the callback function.
           *
           * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments}
           * @type {Array}
           */
          var args = Array.prototype.slice.call(arguments, 1);

          if (angular.isDefined(callback)) {
            scope.$evalAsync(function () {
              if (angular.isFunction(callback)) {
                callback(args);
              } else {
                throw new Error('ui-ace use a function as callback.');
              }
            });
          }
        };

        /**
         * Listener factory. Until now only change listeners can be created.
         * @type object
         */
        var listenerFactory = {
          /**
           * Creates a change listener which propagates the change event
           * and the editor session to the callback from the user option
           * onChange. It might be exchanged during runtime, if this
           * happens the old listener will be unbound.
           *
           * @param callback callback function defined in the user options
           * @see onChangeListener
           */
          onChange: function (callback) {
            return function (e) {
              var newValue = session.getValue();

              if (ngModel && newValue !== ngModel.$viewValue &&
                  // HACK make sure to only trigger the apply outside of the
                  // digest loop 'cause ACE is actually using this callback
                  // for any text transformation !
                  !scope.$$phase && !scope.$root.$$phase) {
                scope.$evalAsync(function () {
                  ngModel.$setViewValue(newValue);
                });
              }

              executeUserCallback(callback, e, acee);
            };
          },
          /**
           * Creates a blur listener which propagates the editor session
           * to the callback from the user option onBlur. It might be
           * exchanged during runtime, if this happens the old listener
           * will be unbound.
           *
           * @param callback callback function defined in the user options
           * @see onBlurListener
           */
          onBlur: function (callback) {
            return function () {
              executeUserCallback(callback, acee);
            };
          }
        };

        attrs.$observe('readonly', function (value) {
          acee.setReadOnly(!!value || value === '');
        });

        // Value Blind
        if (ngModel) {
          ngModel.$formatters.push(function (value) {
            if (angular.isUndefined(value) || value === null) {
              return '';
            }
            else if (angular.isObject(value) || angular.isArray(value)) {
              throw new Error('ui-ace cannot use an object or an array as a model');
            }
            return value;
          });

          ngModel.$render = function () {
            session.setValue(ngModel.$viewValue);
          };
        }

        // Listen for option updates
        var updateOptions = function (current, previous) {
          if (current === previous) return;
          opts = angular.extend({}, options, scope.$eval(attrs.uiAce));

          opts.callbacks = [ opts.onLoad ];
          if (opts.onLoad !== options.onLoad) {
            // also call the global onLoad handler
            opts.callbacks.unshift(options.onLoad);
          }

          // EVENTS

          // unbind old change listener
          session.removeListener('change', onChangeListener);

          // bind new change listener
          onChangeListener = listenerFactory.onChange(opts.onChange);
          session.on('change', onChangeListener);

          // unbind old blur listener
          //session.removeListener('blur', onBlurListener);
          acee.removeListener('blur', onBlurListener);

          // bind new blur listener
          onBlurListener = listenerFactory.onBlur(opts.onBlur);
          acee.on('blur', onBlurListener);

          setOptions(acee, session, opts);
        };

        scope.$watch(attrs.uiAce, updateOptions, /* deep watch */ true);

        // set the options here, even if we try to watch later, if this
        // line is missing things go wrong (and the tests will also fail)
        updateOptions(options);

        elm.on('$destroy', function () {
          acee.session.$stopWorker();
          acee.destroy();
        });

        scope.$watch(function() {
          return [elm[0].offsetWidth, elm[0].offsetHeight];
        }, function() {
          acee.resize();
          acee.renderer.updateFull();
        }, true);

      }
    };
  }]);
<commit_msg>add changeAnnotation listener<commit_after>'use strict';

/**
 * Binds a ACE Editor widget
 */
angular.module('ui.ace', [])
  .constant('uiAceConfig', {})
  .directive('uiAce', ['uiAceConfig', function (uiAceConfig) {

    if (angular.isUndefined(window.ace)) {
      throw new Error('ui-ace need ace to work... (o rly?)');
    }

    /**
     * Sets editor options such as the wrapping mode or the syntax checker.
     *
     * The supported options are:
     *
     *

     *
showGutter

     *
useWrapMode

     *
onLoad

     *
theme

     *
mode

     *

     *
     * @param acee
     * @param session ACE editor session
     * @param {object} opts Options to be set
     */
    var setOptions = function(acee, session, opts) {

      // sets the ace worker path, if running from concatenated
      // or minified source
      if (angular.isDefined(opts.workerPath)) {
        var config = window.ace.require('ace/config');
        config.set('workerPath', opts.workerPath);
      }
      // ace requires loading
      if (angular.isDefined(opts.require)) {
        opts.require.forEach(function (n) {
            window.ace.require(n);
        });
      }
      // Boolean options
      if (angular.isDefined(opts.showGutter)) {
        acee.renderer.setShowGutter(opts.showGutter);
      }
      if (angular.isDefined(opts.useWrapMode)) {
        session.setUseWrapMode(opts.useWrapMode);
      }
      if (angular.isDefined(opts.showInvisibles)) {
        acee.renderer.setShowInvisibles(opts.showInvisibles);
      }
      if (angular.isDefined(opts.showIndentGuides)) {
        acee.renderer.setDisplayIndentGuides(opts.showIndentGuides);
      }
      if (angular.isDefined(opts.useSoftTabs)) {
        session.setUseSoftTabs(opts.useSoftTabs);
      }
      if (angular.isDefined(opts.showPrintMargin)) {
        acee.setShowPrintMargin(opts.showPrintMargin);
      }

      // commands
      if (angular.isDefined(opts.disableSearch) && opts.disableSearch) {
        acee.commands.addCommands([
          {
            name: 'unfind',
            bindKey: {
              win: 'Ctrl-F',
              mac: 'Command-F'
            },
            exec: function () {
              return false;
            },
            readOnly: true
          }
        ]);
      }

      // Basic options
      if (angular.isString(opts.theme)) {
        acee.setTheme('ace/theme/' + opts.theme);
      }
      if (angular.isString(opts.mode)) {
        session.setMode('ace/mode/' + opts.mode);
      }
      // Advanced options
      if (angular.isDefined(opts.firstLineNumber)) {
        if (angular.isNumber(opts.firstLineNumber)) {
          session.setOption('firstLineNumber', opts.firstLineNumber);
        } else if (angular.isFunction(opts.firstLineNumber)) {
          session.setOption('firstLineNumber', opts.firstLineNumber());
        }
      }

      // advanced options
      var key, obj;
      if (angular.isDefined(opts.advanced)) {
          for (key in opts.advanced) {
              // create a javascript object with the key and value
              obj = { name: key, value: opts.advanced[key] };
              // try to assign the option to the ace editor
              acee.setOption(obj.name, obj.value);
          }
      }

      // advanced options for the renderer
      if (angular.isDefined(opts.rendererOptions)) {
          for (key in opts.rendererOptions) {
              // create a javascript object with the key and value
              obj = { name: key, value: opts.rendererOptions[key] };
              // try to assign the option to the ace editor
              acee.renderer.setOption(obj.name, obj.value);
          }
      }

      // onLoad callbacks
      angular.forEach(opts.callbacks, function (cb) {
        if (angular.isFunction(cb)) {
          cb(acee);
        }
      });
    };

    return {
      restrict: 'EA',
      require: '?ngModel',
      link: function (scope, elm, attrs, ngModel) {

        /**
         * Corresponds the uiAceConfig ACE configuration.
         * @type object
         */
        var options = uiAceConfig.ace || {};

        /**
         * uiAceConfig merged with user options via json in attribute or data binding
         * @type object
         */
        var opts = angular.extend({}, options, scope.$eval(attrs.uiAce));

        /**
         * ACE editor
         * @type object
         */
        var acee = window.ace.edit(elm[0]);

        /**
         * ACE editor session.
         * @type object
         * @see [EditSession]{@link http://ace.c9.io/#nav=api&api=edit_session}
         */
        var session = acee.getSession();

        /**
         * Reference to a change listener created by the listener factory.
         * @function
         * @see listenerFactory.onChange
         */
        var onChangeListener;

        /**
         * Reference to a change annotation listener created by the listener factory.
         * @function
         * @see listenerFactory.onChangeAnnotation
         */
        var onChangeAnnotationListener;

        /**
         * Reference to a blur listener created by the listener factory.
         * @function
         * @see listenerFactory.onBlur
         */
        var onBlurListener;

        /**
         * Calls a callback by checking its existing. The argument list
         * is variable and thus this function is relying on the arguments
         * object.
         * @throws {Error} If the callback isn't a function
         */
        var executeUserCallback = function () {

          /**
           * The callback function grabbed from the array-like arguments
           * object. The first argument should always be the callback.
           *
           * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments}
           * @type {*}
           */
          var callback = arguments[0];

          /**
           * Arguments to be passed to the callback. These are taken
           * from the array-like arguments object. The first argument
           * is stripped because that should be the callback function.
           *
           * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments}
           * @type {Array}
           */
          var args = Array.prototype.slice.call(arguments, 1);

          if (angular.isDefined(callback)) {
            scope.$evalAsync(function () {
              if (angular.isFunction(callback)) {
                callback(args);
              } else {
                throw new Error('ui-ace use a function as callback.');
              }
            });
          }
        };

        /**
         * Listener factory. Until now only change listeners can be created.
         * @type object
         */
        var listenerFactory = {
          /**
           * Creates a change listener which propagates the change event
           * and the editor session to the callback from the user option
           * onChange. It might be exchanged during runtime, if this
           * happens the old listener will be unbound.
           *
           * @param callback callback function defined in the user options
           * @see onChangeListener
           */
          onChange: function (callback) {
            return function (e) {
              var newValue = session.getValue();

              if (ngModel && newValue !== ngModel.$viewValue &&
                  // HACK make sure to only trigger the apply outside of the
                  // digest loop 'cause ACE is actually using this callback
                  // for any text transformation !
                  !scope.$$phase && !scope.$root.$$phase) {
                scope.$evalAsync(function () {
                  ngModel.$setViewValue(newValue);
                });
              }

              executeUserCallback(callback, e, acee);
            };
          },
          /**
           * Creates an annotation listener which propagates the editor session
           * to the callback from the user option onChangeAnnotation. It might be
           * exchanged during runtime, if this happens the old listener
           * will be unbound.
           *
           * @param callback callback function defined in the user options
           * @see onChangeAnnotation
           */
          onChangeAnnotation: function(callback) {
            return function() {
              callback(session.getAnnotations());
            }
          },
          /**
           * Creates a blur listener which propagates the editor session
           * to the callback from the user option onBlur. It might be
           * exchanged during runtime, if this happens the old listener
           * will be unbound.
           *
           * @param callback callback function defined in the user options
           * @see onBlurListener
           */
          onBlur: function (callback) {
            return function () {
              executeUserCallback(callback, acee);
            };
          }
        };

        attrs.$observe('readonly', function (value) {
          acee.setReadOnly(!!value || value === '');
        });

        // Value Blind
        if (ngModel) {
          ngModel.$formatters.push(function (value) {
            if (angular.isUndefined(value) || value === null) {
              return '';
            }
            else if (angular.isObject(value) || angular.isArray(value)) {
              throw new Error('ui-ace cannot use an object or an array as a model');
            }
            return value;
          });

          ngModel.$render = function () {
            session.setValue(ngModel.$viewValue);
          };
        }

        // Listen for option updates
        var updateOptions = function (current, previous) {
          if (current === previous) return;
          opts = angular.extend({}, options, scope.$eval(attrs.uiAce));

          opts.callbacks = [ opts.onLoad ];
          if (opts.onLoad !== options.onLoad) {
            // also call the global onLoad handler
            opts.callbacks.unshift(options.onLoad);
          }

          // EVENTS

          // unbind old change listener
          session.removeListener('change', onChangeListener);

          // bind new change listener
          onChangeListener = listenerFactory.onChange(opts.onChange);
          session.on('change', onChangeListener);

          // unbind old annotation listener
          //session.removeListener('blur', onChangeAnnotationListener);
          session.removeListener('changeAnnotation', onChangeAnnotationListener);

          // bind new blur listener
          onChangeAnnotationListener = listenerFactory.onChangeAnnotation(opts.onChangeAnnotation);
          session.on('changeAnnotation', onChangeAnnotationListener);

          // unbind old blur listener
          //session.removeListener('blur', onBlurListener);
          acee.removeListener('blur', onBlurListener);

          // bind new blur listener
          onBlurListener = listenerFactory.onBlur(opts.onBlur);
          acee.on('blur', onBlurListener);

          setOptions(acee, session, opts);
        };

        scope.$watch(attrs.uiAce, updateOptions, /* deep watch */ true);

        // set the options here, even if we try to watch later, if this
        // line is missing things go wrong (and the tests will also fail)
        updateOptions(options);

        elm.on('$destroy', function () {
          acee.session.$stopWorker();
          acee.destroy();
        });

        scope.$watch(function() {
          return [elm[0].offsetWidth, elm[0].offsetHeight];
        }, function() {
          acee.resize();
          acee.renderer.updateFull();
        }, true);

      }
    };
  }]);
<|endoftext|>


<commit_before>App.Router.map(function () {
	this.route('about');
	this.route('facebook-login-problem');
	this.route('google-login-problem');
	this.route('play', { path: '/play/:id_concept' });
	this.route('result', { path: '/result/:id_concept' });
	this.route('concepts');
	this.route('user', function () {
		this.route('login');
		this.route('logout');
		this.route('register');
	});

});

App.ApplicationRoute = Ember.Route.extend({
	init: function() {
		this._super();
		// Instantiate authentication manager to handle user login/logout management
		App.AuthManager = AuthManager.create();
		App.Feedback = Feedback.create();
		// Prepare translator, helpers and load language
		App.Translator = Translator.create();
		App.TranslateHelper = Ember.Helper.helper(function (params, hash) {
			return App.Translator.translate(params[0]);
		});
		App.Translator.change(App.Languages.default, App.Translations[App.Languages.default]);
	},
	actions: {
		openContactModal: function (value) {
			this.controller.set('contactModal', true);
			if (typeof value !== 'undefined') {
				this.controller.set('contactContent', value);
			}
			$("body").animate({
				scrollTop: 0
			}, 400);
		},
		closeContactModal: function () {
			this.controller.set('contactModal', false);
			this.controller.set('contactContent', '');
		},
		changeLanguage: function (language) {
			// TODO: finish switching languages and redrawing
		}
	}
});
App.IndexRoute = Ember.Route.extend({
	model: App.Concept.getQuick
});

App.ConceptsRoute = Ember.Route.extend({
	model: App.Concept.getAll,
	resetController: function (controller, isExiting, transition) {
		if (isExiting) {
			controller.set('invalid', null);
			controller.set('interruption', null);
		}
	}
});

App.ResultRoute = Ember.Route.extend({
	model: function(params) {
		return App.Concept.get(params.id_concept);
	}
});

App.PlayRoute = Ember.Route.extend({
	shortcutHandler: null,
	controller: null,
	model: function (params) {
		return App.Play.getQuestions(params.id_concept);
	},
	setupController: function (controller, model) {
		this.set('shortcutHandler', controller.shortcutTriggered);
		this.set('controller', controller);
		// Check if the request went OK and that there are some data at all
		if (typeof model === "undefined" || model === null || model.hasOwnProperty('error') || (model.hasOwnProperty('questions') && model.questions.length == 0)) {
			controller.transitionToRoute('concepts', {queryParams: {invalid: true, interruption: null}});
			return;
		}

		controller.set('roundIdentification', App.Utils.getRoundIdentification());
		controller.set('model', model);
		controller.set('failureDetected', false);
		controller.set('questionCurrent', 1);
		controller.set('answered', false);
		controller.set('questionMaxCount', model.count);
		if (typeof model.concept === 'undefined') {
			controller.set('id_concept', 'mix');
		} else {
			controller.set('id_concept', model.concept.id_concept);
		}
	},
	shortcuts: {
		'num1': 'handleShortcut',
		'num2': 'handleShortcut',
		'num3': 'handleShortcut',
		'num4': 'handleShortcut',
		'enter': 'handleShortcut',
		'escape': 'handleShortcut',
	},
	actions: {
		handleShortcut: function (event) {
			var handler = this.shortcutHandler;
			var controller = this.controller;
			if (handler && controller) {
				handler(event, controller);
			}
		}
	}
});

App.UserLoginRoute = Ember.Route.extend({
	setupController: function (controller, model) {
		// Check if the user is already logged in
		if (!App.AuthManager.isAnonymous()) {
			controller.transitionToRoute('index');
		} else {
			controller.setProperties({
				logged: false,
				errors: [],
				result: null
			});
		}
	}
});

App.UserRegisterRoute = Ember.Route.extend({
	setupController: function (controller, model) {
		// Check if the user is already logged in
		if (!App.AuthManager.isAnonymous()) {
			controller.transitionToRoute('index');
		} else {
			controller.setProperties({
				logged: false,
				errors: []
			});
		}
	}
});

App.UserLogoutRoute = Ember.Route.extend({
	setupController: function (controller, model) {
		// Check if the user is already logged in
		if (!App.AuthManager.isAnonymous()) {
			App.AuthManager.logout();
		} else {
			controller.transitionToRoute('index');
		}
	}
});<commit_msg>Fix missing time on first answer.<commit_after>App.Router.map(function () {
	this.route('about');
	this.route('facebook-login-problem');
	this.route('google-login-problem');
	this.route('play', { path: '/play/:id_concept' });
	this.route('result', { path: '/result/:id_concept' });
	this.route('concepts');
	this.route('user', function () {
		this.route('login');
		this.route('logout');
		this.route('register');
	});

});

App.ApplicationRoute = Ember.Route.extend({
	init: function() {
		this._super();
		// Instantiate authentication manager to handle user login/logout management
		App.AuthManager = AuthManager.create();
		App.Feedback = Feedback.create();
		// Prepare translator, helpers and load language
		App.Translator = Translator.create();
		App.TranslateHelper = Ember.Helper.helper(function (params, hash) {
			return App.Translator.translate(params[0]);
		});
		App.Translator.change(App.Languages.default, App.Translations[App.Languages.default]);
	},
	actions: {
		openContactModal: function (value) {
			this.controller.set('contactModal', true);
			if (typeof value !== 'undefined') {
				this.controller.set('contactContent', value);
			}
			$("body").animate({
				scrollTop: 0
			}, 400);
		},
		closeContactModal: function () {
			this.controller.set('contactModal', false);
			this.controller.set('contactContent', '');
		},
		changeLanguage: function (language) {
			// TODO: finish switching languages and redrawing
		}
	}
});
App.IndexRoute = Ember.Route.extend({
	model: App.Concept.getQuick
});

App.ConceptsRoute = Ember.Route.extend({
	model: App.Concept.getAll,
	resetController: function (controller, isExiting, transition) {
		if (isExiting) {
			controller.set('invalid', null);
			controller.set('interruption', null);
		}
	}
});

App.ResultRoute = Ember.Route.extend({
	model: function(params) {
		return App.Concept.get(params.id_concept);
	}
});

App.PlayRoute = Ember.Route.extend({
	shortcutHandler: null,
	controller: null,
	model: function (params) {
		return App.Play.getQuestions(params.id_concept);
	},
	setupController: function (controller, model) {
		this.set('shortcutHandler', controller.shortcutTriggered);
		this.set('controller', controller);
		// Check if the request went OK and that there are some data at all
		if (typeof model === "undefined" || model === null || model.hasOwnProperty('error') || (model.hasOwnProperty('questions') && model.questions.length == 0)) {
			controller.transitionToRoute('concepts', {queryParams: {invalid: true, interruption: null}});
			return;
		}

		controller.set('roundIdentification', App.Utils.getRoundIdentification());
		controller.set('model', model);
		controller.set('failureDetected', false);
		controller.set('questionCurrent', 1);
		controller.set('answered', false);
		controller.set('questionMaxCount', model.count);
		if (typeof model.concept === 'undefined') {
			controller.set('id_concept', 'mix');
		} else {
			controller.set('id_concept', model.concept.id_concept);
		}
		App.Timetracking.start('question');
	},
	shortcuts: {
		'num1': 'handleShortcut',
		'num2': 'handleShortcut',
		'num3': 'handleShortcut',
		'num4': 'handleShortcut',
		'enter': 'handleShortcut',
		'escape': 'handleShortcut',
	},
	actions: {
		handleShortcut: function (event) {
			var handler = this.shortcutHandler;
			var controller = this.controller;
			if (handler && controller) {
				handler(event, controller);
			}
		}
	}
});

App.UserLoginRoute = Ember.Route.extend({
	setupController: function (controller, model) {
		// Check if the user is already logged in
		if (!App.AuthManager.isAnonymous()) {
			controller.transitionToRoute('index');
		} else {
			controller.setProperties({
				logged: false,
				errors: [],
				result: null
			});
		}
	}
});

App.UserRegisterRoute = Ember.Route.extend({
	setupController: function (controller, model) {
		// Check if the user is already logged in
		if (!App.AuthManager.isAnonymous()) {
			controller.transitionToRoute('index');
		} else {
			controller.setProperties({
				logged: false,
				errors: []
			});
		}
	}
});

App.UserLogoutRoute = Ember.Route.extend({
	setupController: function (controller, model) {
		// Check if the user is already logged in
		if (!App.AuthManager.isAnonymous()) {
			App.AuthManager.logout();
		} else {
			controller.transitionToRoute('index');
		}
	}
});<|endoftext|>


<commit_before>var boom       = require('boom')
  , braveHapi  = require('../brave-hapi')
  , Joi        = require('joi')
  , underscore = require('underscore')
  ;

var v0 = {};

/*
   GET  /ad-manfest?since={milliseconds since epoch}&limit={positive integer}
        defaults to since=0 limit=100

   GET  /ad-manifest?id={database key}&limit={positive integer}
        no default for id, limit=1

 */

v0.get =
{ handler           : function (runtime) {
    return async function (request, reply) {
        var modifiers, query, result
          , debug    = braveHapi.debug(module, request)
          , id       = request.query.id
          , limit    = request.query.limit
          , since    = request.query.since
          , siteInfo = runtime.db.get('site_info')
          ;

        if (id) {
            if (since) { return reply(boom.badData('since and id parameters may not both be present', request.query)); }

            if (!limit || limit === 1) {
              result = await siteInfo.findOne({ _id: id });
              return reply(underscore.omit(result || {}, '_id'));
            }

            query = { _id: { $gte: siteInfo.oid(id) } };
            modifiers = { sort: { _id: 1 } };
        } else {
            since = since || 0;
            limit = 100;
            query = { lastUpdated: { $gte: new Date(since).getTime() } };
            modifiers = { sort: { lastUpdated: 1 } };
        }

        result = await siteInfo.find(query, underscore.extend({ limit: limit }, modifiers));
        result = underscore.map(result, function(entry) { return underscore.omit(entry, '_id'); });

        debug('result=', result);
        reply(result);
    };
  }

, validate          :
  { query           :
    { id            : Joi.string().hex().optional()
    , limit         : Joi.number().positive().optional()
    , since         : Joi.date().format('x').optional()
    }
  }
};

/*
   GET  /ad-manifest/{hostname}
 */

v0.getHostname =
{ handler           : function (runtime) {
    return async function (request, reply) {
        var result
          , debug    = braveHapi.debug(module, request)
          , hostname = request.params.hostname
          , siteInfo = runtime.db.get('site_info')
          ;

        result = await siteInfo.findOne({ hostname : hostname });
        if (!result) { return reply(boom.notFound('', { hostname: hostname })); }

        debug('result=', result);
        reply(result);
    };
  }

, validate          :
  { params          :
    { hostname      : Joi.string().hostname().required()
    }
  }
};

/*
   POST /ad-manifest
        { "hostname": "...", "replacementAd": "..." }
        create (entry MUST not exist)
 */

v0.post =
{ handler           : function (runtime) {
    return async function (request, reply) {
        var result
          , debug    = braveHapi.debug(module, request)
          , state    = request.payload
          , siteInfo = runtime.db.get('site_info')
          ;

        try {
            await siteInfo.insert(underscore.extend(state, { lastUpdated: new Date().getTime() }));
        } catch(ex) {
            debug('insert error', ex);
            return reply(boom.badData('entry already exists', { hostname: state.hostname }));
        }

        result = await siteInfo.findOne({ hostname: state.hostname });
        if (!result) { return reply(boom.badImplementation('database lookup failed', { hostname: state.hostname })); }

        debug('result=', result);
        reply(result);
    };
  }

, validate          :
  { payload         :
    { hostname      : Joi.string().hostname().required()
    , replacementAd : Joi.array().items(Joi.object().keys({ width     : Joi.number().positive().required()
                                                          , height    : Joi.number().positive().required()
                                                          , replaceId : Joi.string().required()
                                                          })).required()
    , lastUpdated   : Joi.any().forbidden()
    }
  }
};

/*
   PUT /ad-manifest/{hostname}
        { "replacementAd": "..." }
        update (entry MUST exist)
 */

v0.putHostname =
{ handler           : function (runtime) {
    return async function (request, reply) {
        var result
          , debug    = braveHapi.debug(module, request)
          , hostname = request.params.hostname
          , state    = request.payload
          , siteInfo = runtime.db.get('site_info')
          ;

        result = await siteInfo.findOne({ hostname: hostname });
        if (!result) { return reply(boom.notFound()); }

        underscore.extend(result, state, { lastUpdated: new Date().getTime() });
        await siteInfo.update({ hostname: hostname }, result, { upsert: true });

        debug('result=', result);
        reply(result);
    };
  }

, validate          :
  { params          :
    { hostname      : Joi.string().hostname().required()
    }
  , payload         :
    { hostname      : Joi.any().forbidden()
    , replacementAd : Joi.array().items(Joi.object().keys({ width     : Joi.number().positive().required()
                                                          , height    : Joi.number().positive().required()
                                                          , replaceId : Joi.string().required()
                                                          })).required()
    , lastUpdated   : Joi.any().forbidden()
    }
  }
};

module.exports.routes =
[ braveHapi.routes.async().path('/ad-manifest').config(v0.get)
, braveHapi.routes.async().path('/ad-manifest/{hostname}').config(v0.getHostname)
, braveHapi.routes.async().post().path('/ad-manifest').config(v0.post)
, braveHapi.routes.async().put().path('/ad-manifest/{hostname}').config(v0.putHostname)
];

module.exports.initialize = async function (debug, runtime) {
    var doneP, indices
      , siteInfo = runtime.db.get('site_info')
      ;

    try { indices = await siteInfo.indexes(); } catch (ex) { indices = []; }
    doneP = underscore.keys(indices).indexOf('hostname_1') !== -1;

    debug('site_info indices ' + (doneP ? 'already' : 'being') + ' created');
    if (doneP) { return; }

    try {
        if (indices.length === 0) { await siteInfo.insert({ hostname: '' }); }

        await siteInfo.index({ hostname : 1 }, { unique : true });
    } catch (ex) {
        debug('unable to create site_info hostname index', ex);
    }
};
<commit_msg>[ad-manifest] return _id as it is needed by the id= parameter<commit_after>var boom       = require('boom')
  , braveHapi  = require('../brave-hapi')
  , Joi        = require('joi')
  , underscore = require('underscore')
  ;

var v0 = {};

/*
   GET  /ad-manfest?since={milliseconds since epoch}&limit={positive integer}
        defaults to since=0 limit=100

   GET  /ad-manifest?id={database key}&limit={positive integer}
        no default for id, limit=1

 */

v0.get =
{ handler           : function (runtime) {
    return async function (request, reply) {
        var modifiers, query, result
          , id       = request.query.id
          , limit    = request.query.limit
          , since    = request.query.since
          , siteInfo = runtime.db.get('site_info')
          ;

        if (id) {
            if (since) { return reply(boom.badData('since and id parameters may not both be present', request.query)); }

            if (!limit || limit === 1) {
              result = await siteInfo.findOne({ _id: id });
              return reply(result || {});
            }

            query = { _id: { $gte: siteInfo.oid(id) } };
            modifiers = { sort: { _id: 1 } };
        } else {
            since = since || 0;
            limit = 100;
            query = { lastUpdated: { $gte: new Date(since).getTime() } };
            modifiers = { sort: { lastUpdated: 1 } };
        }

        result = await siteInfo.find(query, underscore.extend({ limit: limit }, modifiers));

        reply(result);
    };
  }

, validate          :
  { query           :
    { id            : Joi.string().hex().optional()
    , limit         : Joi.number().positive().optional()
    , since         : Joi.date().format('x').optional()
    }
  }
};

/*
   GET  /ad-manifest/{hostname}
 */

v0.getHostname =
{ handler           : function (runtime) {
    return async function (request, reply) {
        var result
          , hostname = request.params.hostname
          , siteInfo = runtime.db.get('site_info')
          ;

        result = await siteInfo.findOne({ hostname : hostname });
        if (!result) { return reply(boom.notFound('', { hostname: hostname })); }

        reply(result);
    };
  }

, validate          :
  { params          :
    { hostname      : Joi.string().hostname().required()
    }
  }
};

/*
   POST /ad-manifest
        { "hostname": "...", "replacementAd": "..." }
        create (entry MUST not exist)
 */

v0.post =
{ handler           : function (runtime) {
    return async function (request, reply) {
        var result
          , debug    = braveHapi.debug(module, request)
          , state    = request.payload
          , siteInfo = runtime.db.get('site_info')
          ;

        try {
            await siteInfo.insert(underscore.extend(state, { lastUpdated: new Date().getTime() }));
        } catch(ex) {
            debug('insert error', ex);
            return reply(boom.badData('entry already exists', { hostname: state.hostname }));
        }

        result = await siteInfo.findOne({ hostname: state.hostname });
        if (!result) { return reply(boom.badImplementation('database lookup failed', { hostname: state.hostname })); }

        reply(result);
    };
  }

, validate          :
  { payload         :
    { hostname      : Joi.string().hostname().required()
    , replacementAd : Joi.array().items(Joi.object().keys({ width     : Joi.number().positive().required()
                                                          , height    : Joi.number().positive().required()
                                                          , replaceId : Joi.string().required()
                                                          })).required()
    , lastUpdated   : Joi.any().forbidden()
    }
  }
};

/*
   PUT /ad-manifest/{hostname}
        { "replacementAd": "..." }
        update (entry MUST exist)
 */

v0.putHostname =
{ handler           : function (runtime) {
    return async function (request, reply) {
        var result
          , hostname = request.params.hostname
          , state    = request.payload
          , siteInfo = runtime.db.get('site_info')
          ;

        result = await siteInfo.findOne({ hostname: hostname });
        if (!result) { return reply(boom.notFound()); }

        underscore.extend(result, state, { lastUpdated: new Date().getTime() });
        await siteInfo.update({ hostname: hostname }, result, { upsert: true });

        reply(result);
    };
  }

, validate          :
  { params          :
    { hostname      : Joi.string().hostname().required()
    }
  , payload         :
    { hostname      : Joi.any().forbidden()
    , replacementAd : Joi.array().items(Joi.object().keys({ width     : Joi.number().positive().required()
                                                          , height    : Joi.number().positive().required()
                                                          , replaceId : Joi.string().required()
                                                          })).required()
    , lastUpdated   : Joi.any().forbidden()
    }
  }
};

module.exports.routes =
[ braveHapi.routes.async().path('/ad-manifest').config(v0.get)
, braveHapi.routes.async().path('/ad-manifest/{hostname}').config(v0.getHostname)
, braveHapi.routes.async().post().path('/ad-manifest').config(v0.post)
, braveHapi.routes.async().put().path('/ad-manifest/{hostname}').config(v0.putHostname)
];

module.exports.initialize = async function (debug, runtime) {
    var doneP, indices
      , siteInfo = runtime.db.get('site_info')
      ;

    try { indices = await siteInfo.indexes(); } catch (ex) { indices = []; }
    doneP = underscore.keys(indices).indexOf('hostname_1') !== -1;

    debug('site_info indices ' + (doneP ? 'already' : 'being') + ' created');
    if (doneP) { return; }

    try {
        if (indices.length === 0) { await siteInfo.insert({ hostname: '' }); }

        await siteInfo.index({ hostname : 1 }, { unique : true });
    } catch (ex) {
        debug('unable to create site_info hostname index', ex);
    }
};
<|endoftext|>