<commit_before>// Copyright 2013-2016 Adam Presley. All rights reserved
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

define(
	[
		"jquery",
		"services/AlertService"
	],
	function($, AlertService) {
		"use strict";

		var service = {
			/**
			 * addSavedSearch will append a new saved search
			 */
			addSavedSearch: function(name, searchCriteria) {
				searchCriteria.name = name;
				var currentSavedSearches = service.retrieveSavedSearches();

				currentSavedSearches.push(searchCriteria);
				service.storeSavedSearches(currentSavedSearches);
			},

			deleteSavedSearch: function(index) {
				var currentSavedSearches = service.retrieveSavedSearches();

				if (index > -1 && index < currentSavedSearches.length) {
					currentSavedSearches.splice(index, 1);
					service.storeSavedSearches(currentSavedSearches);
				}
			},

			/**
			 * getSavedSearchByIndex returns a saved search based on its location
			 * in the array.
			 */
			getSavedSearchByIndex: function(index) {
				var searches = service.retrieveSavedSearches();

				if (index < 0 && index >= searches.length) {
					AlertService.error({ message: "There is no saved search by that ID" });
					return {
						name: "",
						searchMessage: ""
					};
				}

				return searches[index];
			},

			/**
			 * getServiceSettings will return the MailSlurper service tier address
			 * and port.
			 */
			getServiceSettings: function() {
				return $.ajax({
					method: "GET",
					url: "/servicesettings"
				});
			},

			/**
			 * getServiceURL returns a fully formatted service URL as a key named
			 * "serviceURL" in the context object.
			 */
			getServiceURL: function(context) {
				var serviceSettings = service.retrieveServiceSettings();
				var serviceAddress = serviceSettings.serviceAddress;
				if (serviceAddress === "0.0.0.0") { serviceAddress = window.location.hostname; }
				var serviceURL = "//" + serviceAddress + ":" + serviceSettings.servicePort;
				return serviceURL;
			},

			/**
			 * getServiceURLNow returns a fully formatted service URL as a key named
			 * "serviceURL" directly instead of via a promise.
			 */
			getServiceURLNow: function() {
				var serviceSettings = service.retrieveServiceSettings();
				var serviceAddress = serviceSettings.serviceAddress;
				if (serviceAddress === "0.0.0.0") { serviceAddress = window.location.hostname; }
				var serviceURL = "//" + serviceAddres + ":" + serviceSettings.servicePort;
				return serviceURL;
			},

			/**
			 * retrieveSavedSearches reads saved searches from local storage
			 */
			retrieveSavedSearches: function() {
				if (localStorage["savedSearches"]) {
					return JSON.parse(localStorage["savedSearches"]);
				} else {
					return [];
				}
			},

			/**
			 * retrieveServiceSettings reads the MailSlurper service settings
			 * from the user's local storage.
			 */
			retrieveServiceSettings: function() {
				return JSON.parse(localStorage["serviceSettings"]);
			},

			/**
			 * retrieveSettings reads user settings from local storage.
			 */
			retrieveSettings: function() {
				if (localStorage["settings"]) {
					return JSON.parse(localStorage["settings"]);
				} else {
					return {
						dateFormat: "YYYY-MM-DD hh:mm A",
						autoRefresh: 0,
						theme: "default"
					};
				}
			},

			/**
			 * serviceSettingsExistInLocalStore returns true/false if the
			 * MailSlurper service settings are in the user's local storage.
			 */
			serviceSettingsExistInLocalStore: function() {
				return (localStorage["serviceSettings"] !== undefined);
			},

			/**
			 * storeSavedSearches stores user saved searches in local storage
			 */
			storeSavedSearches: function(savedSearches) {
				localStorage["savedSearches"] = JSON.stringify(savedSearches);
			},

			/**
			 * storeServiceSettings writes the MailSlurper service settings to
			 * the user's local storage.
			 */
			storeServiceSettings: function(serviceSettings) {
				localStorage["serviceSettings"] = JSON.stringify(serviceSettings);
			},

			/**
			 * storeSettings writes user settings to local storage
			 */
			storeSettings: function(settings) {
				localStorage["settings"] = JSON.stringify(settings);
			}
		};

		return service;
	}
);
<commit_msg>And this is why I prefer compiled language ...<commit_after>// Copyright 2013-2016 Adam Presley. All rights reserved
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

define(
	[
		"jquery",
		"services/AlertService"
	],
	function($, AlertService) {
		"use strict";

		var service = {
			/**
			 * addSavedSearch will append a new saved search
			 */
			addSavedSearch: function(name, searchCriteria) {
				searchCriteria.name = name;
				var currentSavedSearches = service.retrieveSavedSearches();

				currentSavedSearches.push(searchCriteria);
				service.storeSavedSearches(currentSavedSearches);
			},

			deleteSavedSearch: function(index) {
				var currentSavedSearches = service.retrieveSavedSearches();

				if (index > -1 && index < currentSavedSearches.length) {
					currentSavedSearches.splice(index, 1);
					service.storeSavedSearches(currentSavedSearches);
				}
			},

			/**
			 * getSavedSearchByIndex returns a saved search based on its location
			 * in the array.
			 */
			getSavedSearchByIndex: function(index) {
				var searches = service.retrieveSavedSearches();

				if (index < 0 && index >= searches.length) {
					AlertService.error({ message: "There is no saved search by that ID" });
					return {
						name: "",
						searchMessage: ""
					};
				}

				return searches[index];
			},

			/**
			 * getServiceSettings will return the MailSlurper service tier address
			 * and port.
			 */
			getServiceSettings: function() {
				return $.ajax({
					method: "GET",
					url: "/servicesettings"
				});
			},

			/**
			 * getServiceURL returns a fully formatted service URL as a key named
			 * "serviceURL" in the context object.
			 */
			getServiceURL: function(context) {
				var serviceSettings = service.retrieveServiceSettings();
				var serviceAddress = serviceSettings.serviceAddress;
				if (serviceAddress === "0.0.0.0") { serviceAddress = window.location.hostname; }
				var serviceURL = "//" + serviceAddress + ":" + serviceSettings.servicePort;
				return serviceURL;
			},

			/**
			 * getServiceURLNow returns a fully formatted service URL as a key named
			 * "serviceURL" directly instead of via a promise.
			 */
			getServiceURLNow: function() {
				var serviceSettings = service.retrieveServiceSettings();
				var serviceAddress = serviceSettings.serviceAddress;
				if (serviceAddress === "0.0.0.0") { serviceAddress = window.location.hostname; }
				var serviceURL = "//" + serviceAddress + ":" + serviceSettings.servicePort;
				return serviceURL;
			},

			/**
			 * retrieveSavedSearches reads saved searches from local storage
			 */
			retrieveSavedSearches: function() {
				if (localStorage["savedSearches"]) {
					return JSON.parse(localStorage["savedSearches"]);
				} else {
					return [];
				}
			},

			/**
			 * retrieveServiceSettings reads the MailSlurper service settings
			 * from the user's local storage.
			 */
			retrieveServiceSettings: function() {
				return JSON.parse(localStorage["serviceSettings"]);
			},

			/**
			 * retrieveSettings reads user settings from local storage.
			 */
			retrieveSettings: function() {
				if (localStorage["settings"]) {
					return JSON.parse(localStorage["settings"]);
				} else {
					return {
						dateFormat: "YYYY-MM-DD hh:mm A",
						autoRefresh: 0,
						theme: "default"
					};
				}
			},

			/**
			 * serviceSettingsExistInLocalStore returns true/false if the
			 * MailSlurper service settings are in the user's local storage.
			 */
			serviceSettingsExistInLocalStore: function() {
				return (localStorage["serviceSettings"] !== undefined);
			},

			/**
			 * storeSavedSearches stores user saved searches in local storage
			 */
			storeSavedSearches: function(savedSearches) {
				localStorage["savedSearches"] = JSON.stringify(savedSearches);
			},

			/**
			 * storeServiceSettings writes the MailSlurper service settings to
			 * the user's local storage.
			 */
			storeServiceSettings: function(serviceSettings) {
				localStorage["serviceSettings"] = JSON.stringify(serviceSettings);
			},

			/**
			 * storeSettings writes user settings to local storage
			 */
			storeSettings: function(settings) {
				localStorage["settings"] = JSON.stringify(settings);
			}
		};

		return service;
	}
);
<|endoftext|>


<commit_before>const webpack = require('webpack');
const path = require('path');
const package = require('../package.json');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const autoprefixer = require('autoprefixer');

const build = (() => {
    const timestamp = new Date().getTime();
    return {
        name: package.name,
        version: package.version,
        timestamp: timestamp,
        author: package.author
    };
})();

const entry = {
    vendor: [
        'react',
        'react-dom',
        'core-js',
        'office-ui-fabric-react'
    ],
    app: [
        'react-hot-loader/patch',
        './index.tsx',
    ],
    'function-file': './function-file/function-file.ts'
};

const rules = [
    {
        test: /\.tsx?$/,
        use: [
            'react-hot-loader/webpack',
            'ts-loader'
        ],
        exclude: /node_modules/
    },
    {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
    },
    {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
    },
    {
        test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
        use: {
            loader: 'file-loader',
            query: {
                name: 'assets/[name].[ext]'
            }
        }
    }
];

const output = {
    path: path.resolve('dist'),
    publicPath: '/',
    filename: '[name].[hash].js',
    chunkFilename: '[id].[hash].chunk.js'
};

const WEBPACK_PLUGINS = [
    new webpack.NamedModulesPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    new webpack.BannerPlugin({ banner: `${build.name} v.${build.version} (${build.timestamp}) © ${build.author}` }),
    new webpack.DefinePlugin({
        ENVIRONMENT: JSON.stringify({
            build: build
        })
    }),
    new webpack.LoaderOptionsPlugin({
        options: {
            postcss: [
                autoprefixer({ browsers: ['Safari >= 8', 'last 2 versions'] }),
            ],
            htmlLoader: {
                minimize: true
            }
        }
    })
];

module.exports = {
    context: path.resolve('./src'),
    entry,
    output,
    resolve: {
        extensions: ['.js', '.jsx', '.ts', '.tsx', '.scss', '.css', '.html']
    },
    module: {
        rules,
    },
    plugins: [
        ...WEBPACK_PLUGINS,
        new ExtractTextPlugin('[name].[hash].css'),
        new HtmlWebpackPlugin({
            title: '<%= projectDisplayName %>',
            filename: 'index.html',
            template: './index.html',
            chunks: ['app', 'vendor', 'polyfills']
        }),
        new HtmlWebpackPlugin({
            title: 'plsgod',
            filename: 'function-file/function-file.html',
            template: './function-file/function-file.html',
            chunks: ['function-file']
        }),
        new CopyWebpackPlugin([
            {
                from: './assets',
                ignore: ['*.scss'],
                to: 'assets',
            }
        ])
    ]
};<commit_msg>Re-added CommonsChunkPlugin with correct options<commit_after>const webpack = require('webpack');
const path = require('path');
const package = require('../package.json');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const autoprefixer = require('autoprefixer');

const build = (() => {
    const timestamp = new Date().getTime();
    return {
        name: package.name,
        version: package.version,
        timestamp: timestamp,
        author: package.author
    };
})();

const entry = {
    vendor: [
        'react',
        'react-dom',
        'core-js',
        'office-ui-fabric-react'
    ],
    app: [
        'react-hot-loader/patch',
        './index.tsx',
    ],
    'function-file': './function-file/function-file.ts'
};

const rules = [
    {
        test: /\.tsx?$/,
        use: [
            'react-hot-loader/webpack',
            'ts-loader'
        ],
        exclude: /node_modules/
    },
    {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
    },
    {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
    },
    {
        test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
        use: {
            loader: 'file-loader',
            query: {
                name: 'assets/[name].[ext]'
            }
        }
    }
];

const output = {
    path: path.resolve('dist'),
    publicPath: '/',
    filename: '[name].[hash].js',
    chunkFilename: '[id].[hash].chunk.js'
};

const WEBPACK_PLUGINS = [
    new webpack.NamedModulesPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    new webpack.BannerPlugin({ banner: `${build.name} v.${build.version} (${build.timestamp}) © ${build.author}` }),
    new webpack.DefinePlugin({
        ENVIRONMENT: JSON.stringify({
            build: build
        })
    }),
    new webpack.LoaderOptionsPlugin({
        options: {
            postcss: [
                autoprefixer({ browsers: ['Safari >= 8', 'last 2 versions'] }),
            ],
            htmlLoader: {
                minimize: true
            }
        }
    }),
    new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
        minChunks: Infinity,
        chunks: ['app']
    })
];

module.exports = {
    context: path.resolve('./src'),
    entry,
    output,
    resolve: {
        extensions: ['.js', '.jsx', '.ts', '.tsx', '.scss', '.css', '.html']
    },
    module: {
        rules,
    },
    plugins: [
        ...WEBPACK_PLUGINS,
        new ExtractTextPlugin('[name].[hash].css'),
        new HtmlWebpackPlugin({
            title: '<%= projectDisplayName %>',
            filename: 'index.html',
            template: './index.html',
            chunks: ['app', 'vendor', 'polyfills']
        }),
        new HtmlWebpackPlugin({
            title: 'plsgod',
            filename: 'function-file/function-file.html',
            template: './function-file/function-file.html',
            chunks: ['function-file']
        }),
        new CopyWebpackPlugin([
            {
                from: './assets',
                ignore: ['*.scss'],
                to: 'assets',
            }
        ])
    ]
};<|endoftext|>


<commit_before>require.config({
  paths: {
    jquery                : '<%= bowerDirectory %>/jquery/jquery',
    underscore            : '<%= bowerDirectory %>/underscore/underscore',
    'underscore.string'   : '<%= bowerDirectory %>/underscore.string/dist/underscore.string.min',
    backbone              : '<%= bowerDirectory %>/backbone/backbone',
    'backbone.wreqr'      : '<%= bowerDirectory %>/marionette/public/javascripts/backbone.wreqr',
    'backbone.babysitter' : '<%= bowerDirectory %>/marionette/public/javascripts/backbone.babysitter',
    'backbone.relational' : '<%= bowerDirectory %>/backbone-relational/backbone-relational',
    'backbone.pageable'   : '<%= bowerDirectory %>/backbone-pageable/lib/backbone-pageable',
    marionette            : '<%= bowerDirectory %>/marionette/lib/core/amd/backbone.marionette',
    backgrid              : '<%= bowerDirectory %>/backgrid/lib/backgrid',
    // bootstrap             : '<%= bowerDirectory %>/twitter/bootstrap',
    jade                  : '<%= bowerDirectory %>/require-jade/jade',
    text                  : '<%= bowerDirectory %>/requirejs-text/text',
    moment                : '<%= bowerDirectory %>/moment/moment',
    app                   : 'app',
    collections           : 'collections',
    lib                   : 'lib',
    models                : 'models',
    templates             : 'templates',
    views                 : 'views'
  },
  shim: {
    underscore: {
      exports: '_'
    },
    'underscore.string': {
      deps: ['underscore'],
      exports: '_.string'
    },
    // bootstrap: {
    //   deps: ['jquery']
    // },
    backbone: {
      deps: ['underscore', 'jquery'],
      exports: 'Backbone'
    },
    'backbone.babysitter': {
      deps: ['backbone']
    },
    'backbone.wreqr': {
      deps: ['backbone', 'backbone.babysitter'],
      exports: 'Backbone.Wreqr'
    },
    'backbone.relational': {
      deps: ['backbone'],
      exports: 'Backbone.RelationalModel'
    },
    // marionette: {
    //   deps: ['backbone', 'backbone.wreqr', 'backbone.babysitter'],
    //   exports: 'Backbone.Marionette'
    // },
    backgrid: {
      deps: ['backbone', 'backbone.pageable'],
      exports: 'Backgrid'
    },
    moment: {
      exports: 'moment'
    }
  },
  deps: [
    // 'jquery',
    // 'bootstrap',
    // 'underscore',
    // 'moment',
    'main'
  ]
});
<commit_msg>Get bootstrap js working<commit_after>require.config({
  paths: {
    jquery                : '<%= bowerDirectory %>/jquery/jquery',
    underscore            : '<%= bowerDirectory %>/underscore/underscore',
    'underscore.string'   : '<%= bowerDirectory %>/underscore.string/dist/underscore.string.min',
    backbone              : '<%= bowerDirectory %>/backbone/backbone',
    'backbone.wreqr'      : '<%= bowerDirectory %>/marionette/public/javascripts/backbone.wreqr',
    'backbone.babysitter' : '<%= bowerDirectory %>/marionette/public/javascripts/backbone.babysitter',
    'backbone.relational' : '<%= bowerDirectory %>/backbone-relational/backbone-relational',
    'backbone.pageable'   : '<%= bowerDirectory %>/backbone-pageable/lib/backbone-pageable',
    marionette            : '<%= bowerDirectory %>/marionette/lib/core/amd/backbone.marionette',
    backgrid              : '<%= bowerDirectory %>/backgrid/lib/backgrid',
    bootstrap             : '<%= bowerDirectory %>/sass-bootstrap/dist/js/bootstrap',
    jade                  : '<%= bowerDirectory %>/require-jade/jade',
    text                  : '<%= bowerDirectory %>/requirejs-text/text',
    moment                : '<%= bowerDirectory %>/moment/moment',
    app                   : 'app',
    collections           : 'collections',
    lib                   : 'lib',
    models                : 'models',
    templates             : 'templates',
    views                 : 'views'
  },
  shim: {
    underscore: {
      exports: '_'
    },
    'underscore.string': {
      deps: ['underscore'],
      exports: '_.string'
    },
    bootstrap: {
      deps: ['jquery']
    },
    backbone: {
      deps: ['underscore', 'jquery'],
      exports: 'Backbone'
    },
    'backbone.babysitter': {
      deps: ['backbone']
    },
    'backbone.wreqr': {
      deps: ['backbone', 'backbone.babysitter'],
      exports: 'Backbone.Wreqr'
    },
    'backbone.relational': {
      deps: ['backbone'],
      exports: 'Backbone.RelationalModel'
    },
    // marionette: {
    //   deps: ['backbone', 'backbone.wreqr', 'backbone.babysitter'],
    //   exports: 'Backbone.Marionette'
    // },
    backgrid: {
      deps: ['backbone', 'backbone.pageable'],
      exports: 'Backgrid'
    },
    moment: {
      exports: 'moment'
    }
  },
  deps: [
    // 'jquery',
    'bootstrap',
    // 'underscore',
    // 'moment',
    'main'
  ]
});
<|endoftext|>


<commit_before>import Promise from 'bluebird';
import Discordie from 'discordie';
import chalk from 'chalk';
import moment from 'moment';
import nconf from 'nconf';
import R from 'ramda';

import './init-config';
import './express';
import './phantom';

import commands from './commands';
import sentry from './sentry';

import { getUserLang } from './redis';

const request = Promise.promisify(require('request'));

// Init
const client = new Discordie();

function callCmd(cmd, name, client, evt, suffix) {
  console.log(`${chalk.blue('[' + moment().format('HH:mm:ss' + ']'))} ${chalk.bold.green(name)}: ${suffix}`);

  function processEntry(entry) {
    // If string or number, send as a message
    if (R.is(String, entry)) evt.message.channel.sendMessage(entry);
    if (R.is(Number, entry)) evt.message.channel.sendMessage(entry);
    // If buffer, send as a file, with a default name
    if (Buffer.isBuffer(entry)) evt.message.channel.uploadFile(entry, 'file.png');
    // If it's an object that contains key 'upload', send file with an optional file name
    // This works for both uploading local files and buffers
    if (R.is(Object, entry) && entry.upload) evt.message.channel.uploadFile(entry.upload, entry.filename);
  }

  getUserLang(evt.message.author.id).then(lang => {
    const cmd_return = cmd(client, evt, suffix, lang);

    // All command returns must be a bluebird promise.
    if (cmd_return instanceof Promise) {
      cmd_return.then(res => {
        // If null, don't do anything.
        if (!res) return;
        // If it's an array, process each entry.
        if (R.is(Array, res)) return R.forEach(processEntry, res);
        // Process single entry
        processEntry(res);
      })
      .catch(err => {
        sentry(err, name);
        evt.message.channel.sendMessage(`Error: ${err.message}`);
      });
    }
  });
}

function onMessage(evt) {
  if (!evt.message) return;
  if (client.User.id === evt.message.author.id) return;

  // Checks for PREFIX
  if (evt.message.content[0] === nconf.get('PREFIX')) {
    const command = evt.message.content.toLowerCase().split(' ')[0].substring(1);
    const suffix = evt.message.content.substring(command.length + 2);
    const cmd = commands[command];

    if (cmd) callCmd(cmd, command, client, evt, suffix);
    return;
  }

  // Checks if bot was mentioned
  if (client.User.isMentioned(evt.message)) {
    const msg_split = evt.message.content.split(' ');

    // If bot was mentioned without a command, then skip.
    if (!msg_split[1]) return;

    const suffix = R.join(' ', R.slice(2, msg_split.length, msg_split));
    let cmd_name = msg_split[1].toLowerCase();
    if (cmd_name[0] === nconf.get('PREFIX')) cmd_name = cmd_name.slice(1);
    const cmd = commands[cmd_name];

    if (cmd) callCmd(cmd, cmd_name, client, evt, suffix);
    return;
  }

  // Check personal messages
  if (evt.message.channel.is_private) {
    // Handle invite links
    if (evt.message.content.indexOf('https://discord.gg/') > -1 || evt.message.content.indexOf('https://discordapp.com/invite/') > -1) {
      return commands.join(client, evt, evt.message.content);
    }

    const msg_split = evt.message.content.split(' ');
    const suffix = R.join(' ', R.slice(1, msg_split.length, msg_split));
    const cmd_name = msg_split[0].toLowerCase();
    const cmd = commands[cmd_name];

    if (cmd) callCmd(cmd, cmd_name, client, evt, suffix);
    return;
  }
}

function carbon() {
  if (nconf.get('CARBON_KEY')) {
    request({
      url: 'https://www.carbonitex.net/discord/data/botdata.php',
      headers: {'content-type': 'application/json'},
      json: {
        key: nconf.get('CARBON_KEY'),
        servercount: client.Guilds.length
      }
    }).catch(console.log);
  }
}
setInterval(() => carbon(), 3600000);

function connect() {
  if (!nconf.get('TOKEN') || !nconf.get('CLIENT_ID')) {
    console.error('Please setup TOKEN and CLIENT_ID in config.js to use Gravebot');
    process.exit(1);
  }

  client.connect({token: nconf.get('TOKEN')});
}

function forceFetchUsers() {
  console.log(chalk.green(`[${moment().format('YYYY-MM-DD HH:mm:ss')}] Force fetching users.`));
  client.Users.fetchMembers();
}

// Listen for events on Discord
client.Dispatcher.on('GATEWAY_READY', () => {
  console.log(chalk.green(`[${moment().format('YYYY-MM-DD HH:mm:ss')}] Started successfully. Connected to ${client.Guilds.length} servers.`));
  setTimeout(() => carbon(), 20000);
  setTimeout(() => forceFetchUsers(), 45000);

  client.Dispatcher.on('MESSAGE_CREATE', onMessage);
  client.Dispatcher.on('MESSAGE_UPDATE', onMessage);
});

client.Dispatcher.on('DISCONNECTED', () => {
  console.log(chalk.yellow(`[${moment().format('YYYY-MM-DD HH:mm:ss')}] Disconnected. Attempting to reconnect...`));
  setTimeout(() => {
    connect();
  }, 2000);
});

connect();
<commit_msg>fix multiple onMessage event calls<commit_after>import Promise from 'bluebird';
import Discordie from 'discordie';
import chalk from 'chalk';
import moment from 'moment';
import nconf from 'nconf';
import R from 'ramda';

import './init-config';
import './express';
import './phantom';

import commands from './commands';
import sentry from './sentry';

import { getUserLang } from './redis';

const request = Promise.promisify(require('request'));

// Init
const client = new Discordie();
let initialized = false;

function callCmd(cmd, name, client, evt, suffix) {
  console.log(`${chalk.blue('[' + moment().format('HH:mm:ss' + ']'))} ${chalk.bold.green(name)}: ${suffix}`);

  function processEntry(entry) {
    // If string or number, send as a message
    if (R.is(String, entry)) evt.message.channel.sendMessage(entry);
    if (R.is(Number, entry)) evt.message.channel.sendMessage(entry);
    // If buffer, send as a file, with a default name
    if (Buffer.isBuffer(entry)) evt.message.channel.uploadFile(entry, 'file.png');
    // If it's an object that contains key 'upload', send file with an optional file name
    // This works for both uploading local files and buffers
    if (R.is(Object, entry) && entry.upload) evt.message.channel.uploadFile(entry.upload, entry.filename);
  }

  getUserLang(evt.message.author.id).then(lang => {
    const cmd_return = cmd(client, evt, suffix, lang);

    // All command returns must be a bluebird promise.
    if (cmd_return instanceof Promise) {
      cmd_return.then(res => {
        // If null, don't do anything.
        if (!res) return;
        // If it's an array, process each entry.
        if (R.is(Array, res)) return R.forEach(processEntry, res);
        // Process single entry
        processEntry(res);
      })
      .catch(err => {
        sentry(err, name);
        evt.message.channel.sendMessage(`Error: ${err.message}`);
      });
    }
  });
}

function onMessage(evt) {
  if (!evt.message) return;
  if (client.User.id === evt.message.author.id) return;

  // Checks for PREFIX
  if (evt.message.content[0] === nconf.get('PREFIX')) {
    const command = evt.message.content.toLowerCase().split(' ')[0].substring(1);
    const suffix = evt.message.content.substring(command.length + 2);
    const cmd = commands[command];

    if (cmd) callCmd(cmd, command, client, evt, suffix);
    return;
  }

  // Checks if bot was mentioned
  if (client.User.isMentioned(evt.message)) {
    const msg_split = evt.message.content.split(' ');

    // If bot was mentioned without a command, then skip.
    if (!msg_split[1]) return;

    const suffix = R.join(' ', R.slice(2, msg_split.length, msg_split));
    let cmd_name = msg_split[1].toLowerCase();
    if (cmd_name[0] === nconf.get('PREFIX')) cmd_name = cmd_name.slice(1);
    const cmd = commands[cmd_name];

    if (cmd) callCmd(cmd, cmd_name, client, evt, suffix);
    return;
  }

  // Check personal messages
  if (evt.message.channel.is_private) {
    // Handle invite links
    if (evt.message.content.indexOf('https://discord.gg/') > -1 || evt.message.content.indexOf('https://discordapp.com/invite/') > -1) {
      return commands.join(client, evt, evt.message.content);
    }

    const msg_split = evt.message.content.split(' ');
    const suffix = R.join(' ', R.slice(1, msg_split.length, msg_split));
    const cmd_name = msg_split[0].toLowerCase();
    const cmd = commands[cmd_name];

    if (cmd) callCmd(cmd, cmd_name, client, evt, suffix);
    return;
  }
}

function carbon() {
  if (nconf.get('CARBON_KEY')) {
    request({
      url: 'https://www.carbonitex.net/discord/data/botdata.php',
      headers: {'content-type': 'application/json'},
      json: {
        key: nconf.get('CARBON_KEY'),
        servercount: client.Guilds.length
      }
    }).catch(console.log);
  }
}
setInterval(() => carbon(), 3600000);

function connect() {
  if (!nconf.get('TOKEN') || !nconf.get('CLIENT_ID')) {
    console.error('Please setup TOKEN and CLIENT_ID in config.js to use Gravebot');
    process.exit(1);
  }

  client.connect({token: nconf.get('TOKEN')});
}

function forceFetchUsers() {
  console.log(chalk.green(`[${moment().format('YYYY-MM-DD HH:mm:ss')}] Force fetching users.`));
  client.Users.fetchMembers();
}

// Listen for events on Discord
client.Dispatcher.on('GATEWAY_READY', () => {
  console.log(chalk.green(`[${moment().format('YYYY-MM-DD HH:mm:ss')}] Started successfully. Connected to ${client.Guilds.length} servers.`));
  setTimeout(() => forceFetchUsers(), 45000);

  if (!initialized) {
    initialized = true;
    setTimeout(() => carbon(), 20000);

    client.Dispatcher.on('MESSAGE_CREATE', onMessage);
    client.Dispatcher.on('MESSAGE_UPDATE', onMessage);
  }
});

client.Dispatcher.on('DISCONNECTED', () => {
  console.log(chalk.yellow(`[${moment().format('YYYY-MM-DD HH:mm:ss')}] Disconnected. Attempting to reconnect...`));
  setTimeout(() => {
    connect();
  }, 2000);
});

connect();
<|endoftext|>


<commit_before>var tellstick = require('./tellstick.js');

var lastTemp;
var currentTemp = 0;

var target = 10.0;

fridgeHighThresh = 19.35
function controlTemperature(temp){
    var deltaTemp = temp-lastTemp;

    if(temp > target ) {
      console.log("Temperatur er: " + temp + " starter kjøleskap")
      tellstick.startFridge();
    }
    if(temp < target){
      console.log("Temperatur er: " + temp + " stopper kjøleskap")
      tellstick.stopFridge();
    }

}

module.exports.controlTemperature = controlTemperature;
<commit_msg>setter måltemp til 12C<commit_after>var tellstick = require('./tellstick.js');

var lastTemp;
var currentTemp = 0;

var target = 12.0;

fridgeHighThresh = 19.35
function controlTemperature(temp){
    var deltaTemp = temp-lastTemp;

    if(temp > target ) {
      console.log("Temperatur er: " + temp + " starter kjøleskap")
      tellstick.startFridge();
    }
    if(temp < target){
      console.log("Temperatur er: " + temp + " stopper kjøleskap")
      tellstick.stopFridge();
    }

}

module.exports.controlTemperature = controlTemperature;
<|endoftext|>


<commit_before>/* global Fae */

'use strict';

Fae.navigation = {
  current_items: [],

  init: function() {
    this.select_current_nav_item();
    this.update_nav_classes();
    this.subnavHighlighter.init();
    this.mobileMenu.init();
    this.utilityNav();
    this.buttonDropdown();
    this.fade_notices();
    this.language.init();
  },

  select_current_nav_item: function() {
    var self = this;
    var current_base_url = window.location.pathname;
    var url_without_edit_new = current_base_url.split('/')[2];
    $('#main_nav a').each(function(){
      var $this = $(this);
      var link = $this.attr('href');
      if ((link.indexOf(url_without_edit_new) > -1) || link === current_base_url) {
        $this.addClass('current');
        self.current_items.push($this);
        return false;
      }
    });
  },

  update_nav_classes: function() {
    var self = this;
    $.each(self.current_items, function(index, $el){
      if ($el.hasClass('main_nav-link')) {
        self.update_first_level($el);
      } else if ($el.hasClass('main_nav-sub-link')) {
        self.update_second_level($el);
      } else if ($el.hasClass('main_nav-third-link')) {
        self.update_third_level($el);
      }
    });
  },

  update_first_level: function($el) {
    $el.closest('li').addClass('main_nav-active-single');
  },

  update_second_level: function($el) {
    $el
      .closest('li').addClass('main_nav-sub-active')
      .closest('.main_nav-accordion').removeClass('main_nav-accordion').addClass('main_nav-active');
  },

  update_third_level: function($el) {
    $el
      .closest('li').addClass('main_nav-third-active')
      .closest('.sub_nav-accordion').removeClass('sub_nav-accordion').addClass('main_nav-sub-active--no_highlight')
      .closest('.main_nav-accordion').removeClass('main_nav-accordion').addClass('main_nav-active');
  },

  // utility nav drop down
  utilityNav: function() {
    $('.utility_nav-user > a, .utility_nav-view > a').on('click', function(e) {
      e.preventDefault();
      e.stopPropagation();
      var $sub_nav = $(this);

      // there could be more than one. so remove all of the clicked statuses and add to the specific one
      $('.utility_nav-clicked').removeClass('utility_nav-clicked');
      $sub_nav.addClass('utility_nav-clicked');

      // assign a once function to close the menus
      $(document).on('click.utility_nav', function(e){
        // as long as the click is not in the menu
        if ($(e.target).closest('.utility_sub_nav').length === 0) {
          // remove the class from the utility nav
          $sub_nav.removeClass('utility_nav-clicked');

          // unbind the click from the document, no need to keep it around.
          $(document).off('click.utility_nav');
        }
      });
    });
  },

  buttonDropdown: function() {

    // button dropdown class toggle
    $(".button-dropdown").click(function(){
      $(this).toggleClass("button-dropdown--opened");
    });

    // button dropdown click anywhere but the dropdown close
    $("body").click(function(e){
      if ($(e.target).closest(".button-dropdown").length === 0) {
        $(".button-dropdown").removeClass("button-dropdown--opened");
      }
    });
  },

  fade_notices: function() {
    $('.notice, .alert, .error').not('.input .error').delay(3000).slideUp('fast');
  },
}
<commit_msg>better js -  thanks tim :boy: - selecting current in new and edit as well<commit_after>/* global Fae */

'use strict';

Fae.navigation = {
  current_items: [],

  init: function() {
    this.select_current_nav_item();
    this.update_nav_classes();
    this.subnavHighlighter.init();
    this.mobileMenu.init();
    this.utilityNav();
    this.buttonDropdown();
    this.fade_notices();
    this.language.init();
  },

  select_current_nav_item: function() {
    var self = this;
    var current_base_url = window.location.pathname;
    var regex_match = current_base_url.match(/^\/admin\/#?(\w*)?\//);
    $('#main_nav a').each(function(){
      var $this = $(this);
      var link = $this.attr('href');
      if ((regex_match != null && link.indexOf(regex_match[1]) > -1) || link === current_base_url) {
        $this.addClass('current');
        self.current_items.push($this);
        return false;
      }
    });
  },

  update_nav_classes: function() {
    var self = this;
    $.each(self.current_items, function(index, $el){
      if ($el.hasClass('main_nav-link')) {
        self.update_first_level($el);
      } else if ($el.hasClass('main_nav-sub-link')) {
        self.update_second_level($el);
      } else if ($el.hasClass('main_nav-third-link')) {
        self.update_third_level($el);
      }
    });
  },

  update_first_level: function($el) {
    $el.closest('li').addClass('main_nav-active-single');
  },

  update_second_level: function($el) {
    $el
      .closest('li').addClass('main_nav-sub-active')
      .closest('.main_nav-accordion').removeClass('main_nav-accordion').addClass('main_nav-active');
  },

  update_third_level: function($el) {
    $el
      .closest('li').addClass('main_nav-third-active')
      .closest('.sub_nav-accordion').removeClass('sub_nav-accordion').addClass('main_nav-sub-active--no_highlight')
      .closest('.main_nav-accordion').removeClass('main_nav-accordion').addClass('main_nav-active');
  },

  // utility nav drop down
  utilityNav: function() {
    $('.utility_nav-user > a, .utility_nav-view > a').on('click', function(e) {
      e.preventDefault();
      e.stopPropagation();
      var $sub_nav = $(this);

      // there could be more than one. so remove all of the clicked statuses and add to the specific one
      $('.utility_nav-clicked').removeClass('utility_nav-clicked');
      $sub_nav.addClass('utility_nav-clicked');

      // assign a once function to close the menus
      $(document).on('click.utility_nav', function(e){
        // as long as the click is not in the menu
        if ($(e.target).closest('.utility_sub_nav').length === 0) {
          // remove the class from the utility nav
          $sub_nav.removeClass('utility_nav-clicked');

          // unbind the click from the document, no need to keep it around.
          $(document).off('click.utility_nav');
        }
      });
    });
  },

  buttonDropdown: function() {

    // button dropdown class toggle
    $(".button-dropdown").click(function(){
      $(this).toggleClass("button-dropdown--opened");
    });

    // button dropdown click anywhere but the dropdown close
    $("body").click(function(e){
      if ($(e.target).closest(".button-dropdown").length === 0) {
        $(".button-dropdown").removeClass("button-dropdown--opened");
      }
    });
  },

  fade_notices: function() {
    $('.notice, .alert, .error').not('.input .error').delay(3000).slideUp('fast');
  },
}
<|endoftext|>


<commit_before>/**
 * class-component.js v4.0.0
 * author: Yoshiya Hinosawa ( http://github.com/kt3k )
 * license: MIT
 */

(function ($) {
    'use strict';

    /**
     Registers a class component of the given name using the given defining function.

     This automatically initializes all `.class-name` elements on the page at `$(document).ready` timing.

     This also registers `init-class.{class-name}` event handler to `document`, which invokes the initialization of the class,
     so if you want to initialize them after `$(document).ready`, you need to trigger `init-class.{class-name}` event on the document.

     The initialization doesn't run over twice for a element.

     @param {String} className The class name
     @param {Function} definition The class definition

     See README.md for examples.
     */
    $.registerClassComponent = function (name, definingFunction) {
        'use strict';

        if (typeof name !== 'string') {

            throw new Error('`name` of a class component has to be a string');

        }

        if (typeof definingFunction !== 'function') {

            throw new Error('`definingFunction` of a class component has to be a string');

        }

        var classComponent = new ClassComponentConfiguration(name, definingFunction);

        var init = function () {

            var elements = $(classComponent.selector()).each(function () {

                classComponent.markInitialized(this);

                classComponent.applyCustomDefinition(this);

            });

            $(document).trigger(classComponent.initStartedEvent(), [elements]);

        };

        $(document).on(classComponent.initEvent(), init);

        $(document).ready(function () {

            $(document).trigger(classComponent.initEvent());

        });

    };

    /**
     * The main namespace for class component modules.
     */
    $.CC = $.registerClassComponent;

    $.CC.register = $.registerClassComponent;

    /**
     * Gets or sets the promise which resolves when the initializaion of the class component is ready.
     *
     * @param {String} className The class name
     * @param {Promise} promise The promise which resolves when the init process finished
     * @return {Promise}
     */
    $.fn.classComponentReady = function (className, promise) {

        if (promise) {

            return this.data('__class_component_init_promise:' + className, promise);

        }

        return this.data('__class_component_init_promise:' + className);

    };

    /**
     ClassComponent is the utility class for class component initialization.

     @class
     */
    function ClassComponentConfiguration(className, definingFunction) {

        this.className = className;
        this.definingFunction = definingFunction;

    };

    var pt = ClassComponentConfiguration.prototype;

    /**
     Returns init event string.

     @return {String}
     */
    pt.initEvent = function () {

        return 'init-class.' + this.className;

    };

    pt.initStartedEvent = function () {

        return 'init-class-started.' + this.className;

    };

    /**
     @private
     @return {String}
     */
    pt.initializedClass = function () {

        return this.className + '-initialized';

    };

    /**
     Returns the selector for uninitialized class component.

     @return {String}
     */
    pt.selector = function () {

        return '.' + this.className + ':not(.' + this.initializedClass() + ')';

    };

    /**
     Marks the given element as initialized as this class component.

     @param {HTMLElement} elem
     */
    pt.markInitialized = function (elem) {

        $(elem).addClass(this.initializedClass());

    };

    /**
     Applies the defining function to the element.

     @param {HTMLElement} elem
     */
    pt.applyCustomDefinition = function (elem) {

        this.definingFunction.call(elem, $(elem));

    };

}(jQuery));
<commit_msg>Create ClassComponentManager and refactor<commit_after>/**
 * class-component.js v4.0.0
 * author: Yoshiya Hinosawa ( http://github.com/kt3k )
 * license: MIT
 */

(function ($) {
    'use strict';

    /**
     Registers a class component of the given name using the given defining function.

     This automatically initializes all `.class-name` elements on the page at `$(document).ready` timing.

     This also registers `init-class.{class-name}` event handler to `document`, which invokes the initialization of the class,
     so if you want to initialize them after `$(document).ready`, you need to trigger `init-class.{class-name}` event on the document.

     The initialization doesn't run over twice for a element.

     @param {String} className The class name
     @param {Function} definition The class definition

     See README.md for examples.
     */
    $.registerClassComponent = function (name, definingFunction) {

        if (typeof name !== 'string') {

            throw new Error('`name` of a class component has to be a string');

        }

        if (typeof definingFunction !== 'function') {

            throw new Error('`definingFunction` of a class component has to be a string');

        }

        ccm.register(name, new ClassComponentConfiguration(name, definingFunction));

        $(document).ready(function () {

            ccm.init(name);

        });

    };

    /**
     * The main namespace for class component modules.
     */
    $.CC = $.registerClassComponent;

    /**
     * Gets or sets the promise which resolves when the initializaion of the class component is ready.
     *
     * @param {String} className The class name
     * @param {Promise} promise The promise which resolves when the init process finished
     * @return {Promise}
     */
    $.fn.classComponentReady = function (className, promise) {

        if (promise) {

            return this.data('__class_component_init_promise:' + className, promise);

        }

        return this.data('__class_component_init_promise:' + className);

    };

    /**
     ClassComponent is the utility class for class component initialization.

     @class
     */
    function ClassComponentConfiguration(className, definingFunction) {

        this.className = className;
        this.definingFunction = definingFunction;

    };

    var pt = ClassComponentConfiguration.prototype;

    /**
     @private
     @return {String}
     */
    pt.initializedClass = function () {

        return this.className + '-initialized';

    };

    /**
     Returns the selector for uninitialized class component.

     @return {String}
     */
    pt.selector = function () {

        return '.' + this.className + ':not(.' + this.initializedClass() + ')';

    };

    /**
     Marks the given element as initialized as this class component.

     @param {HTMLElement} elem
     */
    pt.markInitialized = function (elem) {

        $(elem).addClass(this.initializedClass());

    };

    /**
     Applies the defining function to the element.

     @param {HTMLElement} elem
     */
    pt.applyCustomDefinition = function (elem) {

        this.definingFunction.call(elem, $(elem));

    };

    /**
     * ClassComponentManger handles the registration and initialization of the class compoents.
     *
     * @class
     */
    var ClassComponentManager = function () {

        /**
         * @property {Object<ClassComponentConfiguration>} ccc
         */
        this.ccc = {};

    };

    var ccmPt = ClassComponentManager.prototype;

    /**
     * Registers the class component configuration for the given name.
     *
     * @param {String} name The name
     * @param {ClassComponentConfiguration} ccc The class component configuration
     */
    ccmPt.register = function (name, ccc) {

        this.ccc[name] = ccc;

    };

    /**
     * Gets the class component of the given name.
     *
     * @param {String} name The name
     * @return {ClassComponentConfiguration}
     */
    ccmPt.get = function (name) {

        return this.ccc[name];

    };

    /**
     * Initializes the class components of the given name on the given dom.
     *
     * @param {String} name The name
     * @param {HTMLElement|String} dom The dom where class componets are initialized
     */
    ccmPt.init = function (name, dom) {

        var ccc = this.ccc[name];

        var elements = $(ccc.selector(), dom).each(function () {

            ccc.markInitialized(this);

            ccc.applyCustomDefinition(this);

        });

    };

    var ccm = new ClassComponentManager();

    $.CC.register = $.registerClassComponent;

    $.CC.initClassComponent = function (name) {

        ccm.init(name);

    };

    $.CC.__manager__ = ccm;

}(jQuery));
<|endoftext|>


<commit_before>odoo.define('barcodes.FormView', function (require) {
"use strict";

var BarcodeEvents = require('barcodes.BarcodeEvents'); // handle to trigger barcode on bus
var concurrency = require('web.concurrency');
var core = require('web.core');
var Dialog = require('web.Dialog');
var FormController = require('web.FormController');
var FormRenderer = require('web.FormRenderer');

var _t = core._t;

FormController.include({
    custom_events: _.extend({}, FormController.prototype.custom_events, {
        activeBarcode: '_barcodeActivated',
    }),

    /**
     * add default barcode commands for from view
     *
     * @override
     */
    init: function () {
        this._super.apply(this, arguments);
        this.activeBarcode = {
            form_view: {
                commands: {
                    'O-CMD.EDIT': this._barcodeEdit.bind(this),
                    'O-CMD.DISCARD': this._barcodeDiscard.bind(this),
                    'O-CMD.SAVE': this._barcodeSave.bind(this),
                    'O-CMD.PREV': this._barcodePagerPrevious.bind(this),
                    'O-CMD.NEXT': this._barcodePagerNext.bind(this),
                    'O-CMD.PAGER-FIRST': this._barcodePagerFirst.bind(this),
                    'O-CMD.PAGER-LAST': this._barcodePagerLast.bind(this),
                },
            },
        };

        this.barcodeMutex = new concurrency.Mutex();
        this._barcodeStartListening();
    },
    /**
     * @override
     */
    destroy: function () {
        this._barcodeStopListening();
        this._super();
    },

    //--------------------------------------------------------------------------
    // Private
    //--------------------------------------------------------------------------

    /**
     * @private
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     * @returns {Deferred}
     */
    _barcodeAddX2MQuantity: function (barcode, activeBarcode) {
        if (this.mode === 'readonly') {
            this.do_warn(_t('Error : Document not editable'),
                _t('To modify this document, please first start edition.'));
            return new $.Deferred().reject();
        }

        var record = this.model.get(this.handle);
        var candidate = this._getBarCodeRecord(record, barcode, activeBarcode);
        if (candidate) {
            return this._barcodeSelectedCandidate(candidate, record, barcode, activeBarcode);
        } else {
            return this._barcodeWithoutCandidate(record, barcode, activeBarcode);
        }
    },
    /**
     * @private
     */
    _barcodeDiscard: function () {
        return this.discardChanges();
    },
    /**
     * @private
     */
    _barcodeEdit: function () {
        return this._setMode('edit');
    },
    /**
     * @private
     */
    _barcodePagerFirst: function () {
        var self = this;
        return this.mutex.exec(function () {}).then(function () {
            self.pager.updateState({
                current_min: 1,
            }, {notifyChange: true});
        });
    },
    /**
     * @private
     */
    _barcodePagerLast: function () {
        var self = this;
        return this.mutex.exec(function () {}).then(function () {
            var state = self.model.get(self.handle, {raw: true});
            self.pager.updateState({
                current_min: state.count,
            }, {notifyChange: true});
        });
    },
    /**
     * @private
     */
    _barcodePagerNext: function () {
        return this.mutex.exec(function () {}).then(this.pager.next.bind(this.pager));
    },
    /**
     * @private
     */
    _barcodePagerPrevious: function () {
        return this.mutex.exec(function () {}).then(this.pager.previous.bind(this.pager));
    },
    /**
     * Returns true iff the given barcode matches the given record (candidate).
     *
     * @private
     * @param {Object} candidate: record in the x2m
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     * @returns {boolean}
     */
    _barcodeRecordFilter: function (candidate, barcode, activeBarcode) {
        return candidate.data.product_barcode === barcode;
    },
    /**
     * @private
     */
    _barcodeSave: function () {
        return this.saveRecord();
    },
    /**
     * @private
     * @param {Object} candidate: record in the x2m
     * @param {Object} current record
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     * @returns {Deferred}
     */
    _barcodeSelectedCandidate: function (candidate, record, barcode, activeBarcode) {
        var changes = {};
        var candidateChanges = {};
        candidateChanges[activeBarcode.quantity] = candidate.data[activeBarcode.quantity] + 1;
        changes[activeBarcode.fieldName] = {
            operation: 'UPDATE',
            id: candidate.id,
            data: candidateChanges,
        };
        return this.model.notifyChanges(this.handle, changes);
    },
    /**
     * @private
     */
    _barcodeStartListening: function () {
        core.bus.on('barcode_scanned', this, this._barcodeScanned);
        core.bus.on('keypress', this, this._quantityListener);
    },
    /**
     * @private
     */
    _barcodeStopListening: function () {
        core.bus.off('barcode_scanned', this, this._barcodeScanned);
        core.bus.off('keypress', this, this._quantityListener);
    },
    /**
     * @private
     * @param {Object} current record
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     * @returns {Deferred}
     */
    _barcodeWithoutCandidate: function (record, barcode, activeBarcode) {
        var changes = {};
        changes[activeBarcode.name] = barcode;
        return this.model.notifyChanges(record.id, changes);
    },
    /**
     * @private
     * @param {Object} current record
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     * @returns {Object|undefined}
     */
    _getBarCodeRecord: function (record, barcode, activeBarcode) {
        var self = this;
        if (!activeBarcode.fieldName || !record.data[activeBarcode.fieldName]) {
            return;
        }
        return _.find(record.data[activeBarcode.fieldName].data, function (record) {
            return self._barcodeRecordFilter(record, barcode, activeBarcode);
        });
    },

    //--------------------------------------------------------------------------
    // Handlers
    //--------------------------------------------------------------------------

    /**
     * The barcode is activate when at least one widget trigger_up 'activeBarcode' event
     * with the widget option
     *
     * @param {OdooEvent} event
     * @param {string} event.data.name: the current field name
     * @param {string} [event.data.fieldName] optional for x2many sub field
     * @param {string} [event.data.quantity] optional field to increase quantity
     * @param {Object} [event.data.commands] optional added methods
     *     can use comand with specific barcode (with ReservedBarcodePrefixes)
     *     or change 'barcode' for all other received barcodes
     *     (e.g.: 'O-CMD.MAIN-MENU': function ..., barcode: function () {...})
     */
    _barcodeActivated: function (event) {
        event.stopPropagation();
        var name = event.data.name;
        this.activeBarcode[name] = {
            name: name,
            handle: this.handle,
            target: event.target,
            widget: event.target.attrs && event.target.attrs.widget,
            setQuantityWithKeypress: !! event.data.setQuantityWithKeypress,
            fieldName: event.data.fieldName,
            quantity: event.data.quantity,
            commands: event.data.commands || {},
            candidate: this.activeBarcode[name] && this.activeBarcode[name].handle === this.handle ?
                this.activeBarcode[name].candidate : null,
        };

        // we want to disable autofocus when activating the barcode to avoid
        // putting the scanned value in the focused field
        this.disableAutofocus = true;
    },
    /**
     * @private
     * @param {string|function} method defined by the commands options
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     * @returns {Deferred}
     */
    _barcodeActiveScanned: function (method, barcode, activeBarcode) {
        var self = this;
        var methodDef;
        var def = new $.Deferred();
        if (typeof method === 'string') {
            methodDef = this[method](barcode, activeBarcode);
        } else {
            methodDef = method.call(this, barcode, activeBarcode);
        }
        methodDef
            .done(function () {
                var record = self.model.get(self.handle);
                var candidate = self._getBarCodeRecord(record, barcode, activeBarcode);
                activeBarcode.candidate = candidate;
            })
            .always(function () {
                def.resolve();
            });
        return def;
    },
    /**
     * Method called when a user scan a barcode, call each method in function of the
     * widget options then update the renderer
     *
     * @private
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {DOM Object} target
     * @returns {Deferred}
     */
    _barcodeScanned: function (barcode, target) {
        var self = this;
        return this.barcodeMutex.exec(function () {
            var prefixed = _.any(BarcodeEvents.ReservedBarcodePrefixes,
                    function (reserved) {return barcode.indexOf(reserved) === 0;});
            var hasCommand = false;
            var defs = [];
            for (var k in self.activeBarcode) {
                var activeBarcode = self.activeBarcode[k];
                // Handle the case where there are several barcode widgets on the same page. Since the
                // event is global on the page, all barcode widgets will be triggered. However, we only
                // want to keep the event on the target widget.
                if (! $.contains(target, self.el)) {
                    continue;
                }

                var methods = self.activeBarcode[k].commands;
                var method = prefixed ? methods[barcode] : methods.barcode;
                if (method) {
                    if (prefixed) {
                        hasCommand = true;
                    }
                    defs.push(self._barcodeActiveScanned(method, barcode, activeBarcode));
                }
            }
            if (prefixed && !hasCommand) {
                self.do_warn(_t('Error : Barcode command is undefined'), barcode);
            }
            return self.alive($.when.apply($, defs)).then(function () {
                if (!prefixed) {
                    // redraw the view if we scanned a real barcode (required if
                    // we manually apply the change in JS, e.g. incrementing the
                    // quantity)
                    self.update({}, {reload: false});
                }
            });
        });
    },
    /**
     * @private
     * @param {KeyEvent} event
     */
    _quantityListener: function (event) {
        var character = String.fromCharCode(event.which);

        // only catch the event if we're not focused in
        // another field and it's a number
        if (!$(event.target).is('body, .modal') || !/[0-9]/.test(character)) {
            return;
        }

        var barcodeInfos = _.filter(this.activeBarcode, 'setQuantityWithKeypress');
        if (!barcodeInfos.length) {
            return;
        }

        if (!_.compact(_.pluck(barcodeInfos, 'candidate')).length) {
            return this.do_warn(_t('Error : No last scanned barcode'),
                _t('To set the quantity please scan a barcode first.'));
        }

        for (var k in this.activeBarcode) {
            if (this.activeBarcode[k].candidate) {
                this._quantityOpenDialog(character, this.activeBarcode[k]);
            }
        }
    },
    /**
     * @private
     * @param {string} character
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     */
    _quantityOpenDialog: function (character, activeBarcode) {
        var self = this;
        var $content = $('<div>').append($('<input>', {type: 'text', class: 'o_set_qty_input'}));
        this.dialog = new Dialog(this, {
            title: _t('Set quantity'),
            buttons: [{text: _t('Select'), classes: 'btn-primary', close: true, click: function () {
                var new_qty = this.$content.find('.o_set_qty_input').val();
                var values = {};
                values[activeBarcode.quantity] = parseFloat(new_qty);
                return self.model.notifyChanges(activeBarcode.candidate.id, values).then(function () {
                    self.update({}, {reload: false});
                });
            }}, {text: _t('Discard'), close: true}],
            $content: $content,
        });
        this.dialog.opened().then(function () {
            // This line set the value of the key which triggered the _set_quantity in the input
            var $input = self.dialog.$('.o_set_qty_input').focus().val(character);
            var $selectBtn = self.dialog.$footer.find('.btn-primary');
            $input.on('keypress', function (event){
                if (event.which === 13) {
                    event.preventDefault();
                    $input.off();
                    $selectBtn.click();
                }
            });
        });
        this.dialog.open();
    },
});

FormRenderer.include({

    //--------------------------------------------------------------------------
    // Private
    //--------------------------------------------------------------------------
    /**
     * trigger_up 'activeBarcode' to Add barcode event handler
     *
     * @private
     * @param {jQueryElement} $button
     * @param {Object} node
     */
    _barcodeButtonHandler: function ($button, node) {
        var commands = {};
        commands.barcode = function () {return $.when();};
        commands['O-BTN.' + node.attrs.barcode_trigger] = function () {
            if (!$button.hasClass('o_invisible_modifier')) {
                $button.click();
            }
            return $.when();
        };
        this.trigger_up('activeBarcode', {
            name: node.attrs.name,
            commands: commands
        });
    },
    /**
     * Add barcode event handler
     *
     * @override
     * @private
     * @param {Object} node
     * @returns {jQueryElement}
     */
    _renderHeaderButton: function (node) {
        var $button = this._super.apply(this, arguments);
        if (node.attrs.barcode_trigger) {
            this._barcodeButtonHandler($button, node);
        }
        return $button;
    },
    /**
     * Add barcode event handler
     *
     * @override
     * @private
     * @param {Object} node
     * @returns {jQueryElement}
     */
    _renderStatButton: function (node) {
        var $button = this._super.apply(this, arguments);
        if (node.attrs.barcode_trigger) {
            this._barcodeButtonHandler($button, node);
        }
        return $button;
    },
    /**
     * Add barcode event handler
     *
     * @override
     * @private
     * @param {Object} node
     * @returns {jQueryElement}
     */
    _renderTagButton: function (node) {
        var $button = this._super.apply(this, arguments);
        if (node.attrs.barcode_trigger) {
            this._barcodeButtonHandler($button, node);
        }
        return $button;
    }
});

BarcodeEvents.ReservedBarcodePrefixes.push('O-BTN');

});
<commit_msg>[FIX] barcodes: Do not trigger useless onchange server side.<commit_after>odoo.define('barcodes.FormView', function (require) {
"use strict";

var BarcodeEvents = require('barcodes.BarcodeEvents'); // handle to trigger barcode on bus
var concurrency = require('web.concurrency');
var core = require('web.core');
var Dialog = require('web.Dialog');
var FormController = require('web.FormController');
var FormRenderer = require('web.FormRenderer');

var _t = core._t;

FormController.include({
    custom_events: _.extend({}, FormController.prototype.custom_events, {
        activeBarcode: '_barcodeActivated',
    }),

    /**
     * add default barcode commands for from view
     *
     * @override
     */
    init: function () {
        this._super.apply(this, arguments);
        this.activeBarcode = {
            form_view: {
                commands: {
                    'O-CMD.EDIT': this._barcodeEdit.bind(this),
                    'O-CMD.DISCARD': this._barcodeDiscard.bind(this),
                    'O-CMD.SAVE': this._barcodeSave.bind(this),
                    'O-CMD.PREV': this._barcodePagerPrevious.bind(this),
                    'O-CMD.NEXT': this._barcodePagerNext.bind(this),
                    'O-CMD.PAGER-FIRST': this._barcodePagerFirst.bind(this),
                    'O-CMD.PAGER-LAST': this._barcodePagerLast.bind(this),
                },
            },
        };

        this.barcodeMutex = new concurrency.Mutex();
        this._barcodeStartListening();
    },
    /**
     * @override
     */
    destroy: function () {
        this._barcodeStopListening();
        this._super();
    },

    //--------------------------------------------------------------------------
    // Private
    //--------------------------------------------------------------------------

    /**
     * @private
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     * @returns {Deferred}
     */
    _barcodeAddX2MQuantity: function (barcode, activeBarcode) {
        if (this.mode === 'readonly') {
            this.do_warn(_t('Error : Document not editable'),
                _t('To modify this document, please first start edition.'));
            return new $.Deferred().reject();
        }

        var record = this.model.get(this.handle);
        var candidate = this._getBarCodeRecord(record, barcode, activeBarcode);
        if (candidate) {
            return this._barcodeSelectedCandidate(candidate, record, barcode, activeBarcode);
        } else {
            return this._barcodeWithoutCandidate(record, barcode, activeBarcode);
        }
    },
    /**
     * @private
     */
    _barcodeDiscard: function () {
        return this.discardChanges();
    },
    /**
     * @private
     */
    _barcodeEdit: function () {
        return this._setMode('edit');
    },
    /**
     * @private
     */
    _barcodePagerFirst: function () {
        var self = this;
        return this.mutex.exec(function () {}).then(function () {
            self.pager.updateState({
                current_min: 1,
            }, {notifyChange: true});
        });
    },
    /**
     * @private
     */
    _barcodePagerLast: function () {
        var self = this;
        return this.mutex.exec(function () {}).then(function () {
            var state = self.model.get(self.handle, {raw: true});
            self.pager.updateState({
                current_min: state.count,
            }, {notifyChange: true});
        });
    },
    /**
     * @private
     */
    _barcodePagerNext: function () {
        return this.mutex.exec(function () {}).then(this.pager.next.bind(this.pager));
    },
    /**
     * @private
     */
    _barcodePagerPrevious: function () {
        return this.mutex.exec(function () {}).then(this.pager.previous.bind(this.pager));
    },
    /**
     * Returns true iff the given barcode matches the given record (candidate).
     *
     * @private
     * @param {Object} candidate: record in the x2m
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     * @returns {boolean}
     */
    _barcodeRecordFilter: function (candidate, barcode, activeBarcode) {
        return candidate.data.product_barcode === barcode;
    },
    /**
     * @private
     */
    _barcodeSave: function () {
        return this.saveRecord();
    },
    /**
     * @private
     * @param {Object} candidate: record in the x2m
     * @param {Object} current record
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     * @returns {Deferred}
     */
    _barcodeSelectedCandidate: function (candidate, record, barcode, activeBarcode) {
        var changes = {};
        var candidateChanges = {};
        candidateChanges[activeBarcode.quantity] = candidate.data[activeBarcode.quantity] + 1;
        changes[activeBarcode.fieldName] = {
            operation: 'UPDATE',
            id: candidate.id,
            data: candidateChanges,
        };
        return this.model.notifyChanges(this.handle, changes, {notifyChange: activeBarcode.notifyChange});
    },
    /**
     * @private
     */
    _barcodeStartListening: function () {
        core.bus.on('barcode_scanned', this, this._barcodeScanned);
        core.bus.on('keypress', this, this._quantityListener);
    },
    /**
     * @private
     */
    _barcodeStopListening: function () {
        core.bus.off('barcode_scanned', this, this._barcodeScanned);
        core.bus.off('keypress', this, this._quantityListener);
    },
    /**
     * @private
     * @param {Object} current record
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     * @returns {Deferred}
     */
    _barcodeWithoutCandidate: function (record, barcode, activeBarcode) {
        var changes = {};
        changes[activeBarcode.name] = barcode;
        return this.model.notifyChanges(record.id, changes);
    },
    /**
     * @private
     * @param {Object} current record
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     * @returns {Object|undefined}
     */
    _getBarCodeRecord: function (record, barcode, activeBarcode) {
        var self = this;
        if (!activeBarcode.fieldName || !record.data[activeBarcode.fieldName]) {
            return;
        }
        return _.find(record.data[activeBarcode.fieldName].data, function (record) {
            return self._barcodeRecordFilter(record, barcode, activeBarcode);
        });
    },

    //--------------------------------------------------------------------------
    // Handlers
    //--------------------------------------------------------------------------

    /**
     * The barcode is activate when at least one widget trigger_up 'activeBarcode' event
     * with the widget option
     *
     * @param {OdooEvent} event
     * @param {string} event.data.name: the current field name
     * @param {string} [event.data.fieldName] optional for x2many sub field
     * @param {boolean} [event.data.notifyChange] optional for x2many sub field
     *     do not trigger on change server side if a candidate has been found
     * @param {string} [event.data.quantity] optional field to increase quantity
     * @param {Object} [event.data.commands] optional added methods
     *     can use comand with specific barcode (with ReservedBarcodePrefixes)
     *     or change 'barcode' for all other received barcodes
     *     (e.g.: 'O-CMD.MAIN-MENU': function ..., barcode: function () {...})
     */
    _barcodeActivated: function (event) {
        event.stopPropagation();
        var name = event.data.name;
        this.activeBarcode[name] = {
            name: name,
            handle: this.handle,
            target: event.target,
            widget: event.target.attrs && event.target.attrs.widget,
            setQuantityWithKeypress: !! event.data.setQuantityWithKeypress,
            fieldName: event.data.fieldName,
            notifyChange: (event.data.notifyChange !== undefined) ? event.data.notifyChange : true,
            quantity: event.data.quantity,
            commands: event.data.commands || {},
            candidate: this.activeBarcode[name] && this.activeBarcode[name].handle === this.handle ?
                this.activeBarcode[name].candidate : null,
        };

        // we want to disable autofocus when activating the barcode to avoid
        // putting the scanned value in the focused field
        this.disableAutofocus = true;
    },
    /**
     * @private
     * @param {string|function} method defined by the commands options
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     * @returns {Deferred}
     */
    _barcodeActiveScanned: function (method, barcode, activeBarcode) {
        var self = this;
        var methodDef;
        var def = new $.Deferred();
        if (typeof method === 'string') {
            methodDef = this[method](barcode, activeBarcode);
        } else {
            methodDef = method.call(this, barcode, activeBarcode);
        }
        methodDef
            .done(function () {
                var record = self.model.get(self.handle);
                var candidate = self._getBarCodeRecord(record, barcode, activeBarcode);
                activeBarcode.candidate = candidate;
            })
            .always(function () {
                def.resolve();
            });
        return def;
    },
    /**
     * Method called when a user scan a barcode, call each method in function of the
     * widget options then update the renderer
     *
     * @private
     * @param {string} barcode sent by the scanner (string generate from keypress series)
     * @param {DOM Object} target
     * @returns {Deferred}
     */
    _barcodeScanned: function (barcode, target) {
        var self = this;
        return this.barcodeMutex.exec(function () {
            var prefixed = _.any(BarcodeEvents.ReservedBarcodePrefixes,
                    function (reserved) {return barcode.indexOf(reserved) === 0;});
            var hasCommand = false;
            var defs = [];
            for (var k in self.activeBarcode) {
                var activeBarcode = self.activeBarcode[k];
                // Handle the case where there are several barcode widgets on the same page. Since the
                // event is global on the page, all barcode widgets will be triggered. However, we only
                // want to keep the event on the target widget.
                if (! $.contains(target, self.el)) {
                    continue;
                }

                var methods = self.activeBarcode[k].commands;
                var method = prefixed ? methods[barcode] : methods.barcode;
                if (method) {
                    if (prefixed) {
                        hasCommand = true;
                    }
                    defs.push(self._barcodeActiveScanned(method, barcode, activeBarcode));
                }
            }
            if (prefixed && !hasCommand) {
                self.do_warn(_t('Error : Barcode command is undefined'), barcode);
            }
            return self.alive($.when.apply($, defs)).then(function () {
                if (!prefixed) {
                    // redraw the view if we scanned a real barcode (required if
                    // we manually apply the change in JS, e.g. incrementing the
                    // quantity)
                    self.update({}, {reload: false});
                }
            });
        });
    },
    /**
     * @private
     * @param {KeyEvent} event
     */
    _quantityListener: function (event) {
        var character = String.fromCharCode(event.which);

        // only catch the event if we're not focused in
        // another field and it's a number
        if (!$(event.target).is('body, .modal') || !/[0-9]/.test(character)) {
            return;
        }

        var barcodeInfos = _.filter(this.activeBarcode, 'setQuantityWithKeypress');
        if (!barcodeInfos.length) {
            return;
        }

        if (!_.compact(_.pluck(barcodeInfos, 'candidate')).length) {
            return this.do_warn(_t('Error : No last scanned barcode'),
                _t('To set the quantity please scan a barcode first.'));
        }

        for (var k in this.activeBarcode) {
            if (this.activeBarcode[k].candidate) {
                this._quantityOpenDialog(character, this.activeBarcode[k]);
            }
        }
    },
    /**
     * @private
     * @param {string} character
     * @param {Object} activeBarcode: options sent by the field who use barcode features
     */
    _quantityOpenDialog: function (character, activeBarcode) {
        var self = this;
        var $content = $('<div>').append($('<input>', {type: 'text', class: 'o_set_qty_input'}));
        this.dialog = new Dialog(this, {
            title: _t('Set quantity'),
            buttons: [{text: _t('Select'), classes: 'btn-primary', close: true, click: function () {
                var new_qty = this.$content.find('.o_set_qty_input').val();
                var values = {};
                values[activeBarcode.quantity] = parseFloat(new_qty);
                return self.model.notifyChanges(activeBarcode.candidate.id, values).then(function () {
                    self.update({}, {reload: false});
                });
            }}, {text: _t('Discard'), close: true}],
            $content: $content,
        });
        this.dialog.opened().then(function () {
            // This line set the value of the key which triggered the _set_quantity in the input
            var $input = self.dialog.$('.o_set_qty_input').focus().val(character);
            var $selectBtn = self.dialog.$footer.find('.btn-primary');
            $input.on('keypress', function (event){
                if (event.which === 13) {
                    event.preventDefault();
                    $input.off();
                    $selectBtn.click();
                }
            });
        });
        this.dialog.open();
    },
});

FormRenderer.include({

    //--------------------------------------------------------------------------
    // Private
    //--------------------------------------------------------------------------
    /**
     * trigger_up 'activeBarcode' to Add barcode event handler
     *
     * @private
     * @param {jQueryElement} $button
     * @param {Object} node
     */
    _barcodeButtonHandler: function ($button, node) {
        var commands = {};
        commands.barcode = function () {return $.when();};
        commands['O-BTN.' + node.attrs.barcode_trigger] = function () {
            if (!$button.hasClass('o_invisible_modifier')) {
                $button.click();
            }
            return $.when();
        };
        this.trigger_up('activeBarcode', {
            name: node.attrs.name,
            commands: commands
        });
    },
    /**
     * Add barcode event handler
     *
     * @override
     * @private
     * @param {Object} node
     * @returns {jQueryElement}
     */
    _renderHeaderButton: function (node) {
        var $button = this._super.apply(this, arguments);
        if (node.attrs.barcode_trigger) {
            this._barcodeButtonHandler($button, node);
        }
        return $button;
    },
    /**
     * Add barcode event handler
     *
     * @override
     * @private
     * @param {Object} node
     * @returns {jQueryElement}
     */
    _renderStatButton: function (node) {
        var $button = this._super.apply(this, arguments);
        if (node.attrs.barcode_trigger) {
            this._barcodeButtonHandler($button, node);
        }
        return $button;
    },
    /**
     * Add barcode event handler
     *
     * @override
     * @private
     * @param {Object} node
     * @returns {jQueryElement}
     */
    _renderTagButton: function (node) {
        var $button = this._super.apply(this, arguments);
        if (node.attrs.barcode_trigger) {
            this._barcodeButtonHandler($button, node);
        }
        return $button;
    }
});

BarcodeEvents.ReservedBarcodePrefixes.push('O-BTN');

});
<|endoftext|>


<commit_before>import Component from '../Component';
import { Spinner } from 'spin.js';

/**
 * The `LoadingIndicator` component displays a loading spinner with spin.js. It
 * may have the following special props:
 *
 * - `size` The spin.js size preset to use. Defaults to 'small'.
 *
 * All other props will be assigned as attributes on the element.
 */
export default class LoadingIndicator extends Component {
  view() {
    const attrs = Object.assign({}, this.props);

    attrs.className = 'LoadingIndicator ' + (attrs.className || '');
    delete attrs.size;

    return
{m.trust(' ')}
;
  }

  config() {
    const options = { zIndex: 'auto', color: this.$().css('color') };

    switch (this.props.size) {
      case 'large':
        Object.assign(options, { lines: 10, length: 8, width: 4, radius: 8 });
        break;

      default:
        Object.assign(options, { lines: 8, length: 4, width: 3, radius: 5 });
    }

    new Spinner(options).spin(this.element);
  }
}
<commit_msg>configure spinner only once<commit_after>import Component from '../Component';
import { Spinner } from 'spin.js';

/**
 * The `LoadingIndicator` component displays a loading spinner with spin.js. It
 * may have the following special props:
 *
 * - `size` The spin.js size preset to use. Defaults to 'small'.
 *
 * All other props will be assigned as attributes on the element.
 */
export default class LoadingIndicator extends Component {
  view() {
    const attrs = Object.assign({}, this.props);

    attrs.className = 'LoadingIndicator ' + (attrs.className || '');
    delete attrs.size;

    return
{m.trust(' ')}
;
  }

  config(isInitialized) {
    if (isInitialized) return;

    const options = { zIndex: 'auto', color: this.$().css('color') };

    switch (this.props.size) {
      case 'large':
        Object.assign(options, { lines: 10, length: 8, width: 4, radius: 8 });
        break;

      default:
        Object.assign(options, { lines: 8, length: 4, width: 3, radius: 5 });
    }

    new Spinner(options).spin(this.element);
  }
}
<|endoftext|>