<commit_before>/*******************************************************************************************************
 * File: hoot.js
 * Project: hootenanny-ui
 * @author Matt Putipong - matt.putipong@radiantsolutions.com on 8/16/18
 *******************************************************************************************************/

import '../../css/hoot/hoot.scss';

import _forEach from 'lodash-es/forEach';

import API                from './managers/api';
import MessageManager     from './managers/messages/messageManager';
import FolderManager      from './managers/folderManager';
import LayerManager       from './managers/layerManager';
import TranslationManager from './managers/translationManager';
import UserManager        from './managers/userManager';
import EventManager       from './managers/eventManager';
import UI                 from './ui/init';
import { tagInfo }        from '../../data/index';
import buildInfo          from './config/buildInfo.json';
import { duration }       from './tools/utilities';

class Hoot {
    constructor() {
        this.api          = new API( this );
        this.message      = new MessageManager( this );
        this.layers       = new LayerManager( this );
        this.folders      = new FolderManager( this );
        this.translations = new TranslationManager( this );
        this.users        = new UserManager( this );
        this.events       = new EventManager();
        this.duration     = duration;
        // this.user
        this.config = {
            tagInfo,
            appInfo: [],
            users: [],
            exportSizeThreshold: null,
            ingestSizeThreshold: null,
            conflateSizeThreshold: null,
            presetMaxDisplayNum: 12,
            privilegeIcons: {
                admin: 'people_alt',
                advanced: 'star'
            }
        };
    }

    async getAboutData() {
        try {
            let info = await Promise.all( [
                this.api.getCoreVersionInfo(),
                this.api.getServicesVersionInfo()
            ] );

            _forEach( info, d => this.config.appInfo.push( d ) );
        } catch ( err ) {
            // this.message.alert( err );
            return Promise.reject( err );
        }

        // build info will always be available
        this.config.appInfo.push( buildInfo );
    }

    async getAllUsers() {
        try {
            let resp = await this.api.getAllUsers();

            this.config.users = {};

            _forEach( resp, user => {
                this.config.users[ user.id ] = user;
            } );
        } catch ( err ) {
            // this.message.alert( err );
            return Promise.reject( err );
        }
    }

    async getMapSizeThresholds() {
        try {
            let thresholds = await this.api.getMapSizeThresholds();

            this.config.exportSizeThreshold   = thresholds.export_threshold;
            this.config.ingestSizeThreshold   = thresholds.ingest_threshold;
            this.config.conflateSizeThreshold = thresholds.conflate_threshold;
        } catch ( err ) {
            // this.message.alert( err );
            return Promise.reject( err );
        }
    }

    async getGrailMetadata() {
        const privileges = this.user().privileges;

        if ( privileges.advanced && privileges.advanced === 'true' ) {
            const { data } = await this.api.grailMetadataQuery();
            this.config.referenceLabel = data.railsLabel;
            this.config.secondaryLabel = data.overpassLabel;
            this.config.maxFeatureCount = Number(data.maxFeatureCount);
        }
    }

    init( context ) {
        let user;

        this.context = context;
        this.user = function () {
            if (!user) {
                user = JSON.parse( context.storage( 'user' ) );
            }

            return user;
        };

        if (!this.context.storage('history')) {
            this.context.storage('history', JSON.stringify({
                'bboxHistory':[]
            }));
        }

        Promise.all( [
            this.getAboutData(),
            this.getAllUsers(),
            this.getMapSizeThresholds(),
            this.getGrailMetadata(),
            this.translations.getTranslations(),
            this.users.init()
        ] );

        this.ui = new UI();
        this.ui.render();

        // prevent this class from being modified in any way.
        // this does not affect children objects
        Object.freeze( this );
    }
}

// Export this class as a "Singleton". When it is imported the very first time,
// it will create a new instance of the object and store it in memory. Every other
// import afterwards will receive this cached object instead of creating a new instance.

// * Note: This is not a true Singleton, but it mimics the Singleton pattern
// because of Node's module caching behavior.
export default new Hoot();
<commit_msg>alternate admin icon<commit_after>/*******************************************************************************************************
 * File: hoot.js
 * Project: hootenanny-ui
 * @author Matt Putipong - matt.putipong@radiantsolutions.com on 8/16/18
 *******************************************************************************************************/

import '../../css/hoot/hoot.scss';

import _forEach from 'lodash-es/forEach';

import API                from './managers/api';
import MessageManager     from './managers/messages/messageManager';
import FolderManager      from './managers/folderManager';
import LayerManager       from './managers/layerManager';
import TranslationManager from './managers/translationManager';
import UserManager        from './managers/userManager';
import EventManager       from './managers/eventManager';
import UI                 from './ui/init';
import { tagInfo }        from '../../data/index';
import buildInfo          from './config/buildInfo.json';
import { duration }       from './tools/utilities';

class Hoot {
    constructor() {
        this.api          = new API( this );
        this.message      = new MessageManager( this );
        this.layers       = new LayerManager( this );
        this.folders      = new FolderManager( this );
        this.translations = new TranslationManager( this );
        this.users        = new UserManager( this );
        this.events       = new EventManager();
        this.duration     = duration;
        // this.user
        this.config = {
            tagInfo,
            appInfo: [],
            users: [],
            exportSizeThreshold: null,
            ingestSizeThreshold: null,
            conflateSizeThreshold: null,
            presetMaxDisplayNum: 12,
            privilegeIcons: {
                admin: 'how_to_reg',
                advanced: 'star'
            }
        };
    }

    async getAboutData() {
        try {
            let info = await Promise.all( [
                this.api.getCoreVersionInfo(),
                this.api.getServicesVersionInfo()
            ] );

            _forEach( info, d => this.config.appInfo.push( d ) );
        } catch ( err ) {
            // this.message.alert( err );
            return Promise.reject( err );
        }

        // build info will always be available
        this.config.appInfo.push( buildInfo );
    }

    async getAllUsers() {
        try {
            let resp = await this.api.getAllUsers();

            this.config.users = {};

            _forEach( resp, user => {
                this.config.users[ user.id ] = user;
            } );
        } catch ( err ) {
            // this.message.alert( err );
            return Promise.reject( err );
        }
    }

    async getMapSizeThresholds() {
        try {
            let thresholds = await this.api.getMapSizeThresholds();

            this.config.exportSizeThreshold   = thresholds.export_threshold;
            this.config.ingestSizeThreshold   = thresholds.ingest_threshold;
            this.config.conflateSizeThreshold = thresholds.conflate_threshold;
        } catch ( err ) {
            // this.message.alert( err );
            return Promise.reject( err );
        }
    }

    async getGrailMetadata() {
        const privileges = this.user().privileges;

        if ( privileges.advanced && privileges.advanced === 'true' ) {
            const { data } = await this.api.grailMetadataQuery();
            this.config.referenceLabel = data.railsLabel;
            this.config.secondaryLabel = data.overpassLabel;
            this.config.maxFeatureCount = Number(data.maxFeatureCount);
        }
    }

    init( context ) {
        let user;

        this.context = context;
        this.user = function () {
            if (!user) {
                user = JSON.parse( context.storage( 'user' ) );
            }

            return user;
        };

        if (!this.context.storage('history')) {
            this.context.storage('history', JSON.stringify({
                'bboxHistory':[]
            }));
        }

        Promise.all( [
            this.getAboutData(),
            this.getAllUsers(),
            this.getMapSizeThresholds(),
            this.getGrailMetadata(),
            this.translations.getTranslations(),
            this.users.init()
        ] );

        this.ui = new UI();
        this.ui.render();

        // prevent this class from being modified in any way.
        // this does not affect children objects
        Object.freeze( this );
    }
}

// Export this class as a "Singleton". When it is imported the very first time,
// it will create a new instance of the object and store it in memory. Every other
// import afterwards will receive this cached object instead of creating a new instance.

// * Note: This is not a true Singleton, but it mimics the Singleton pattern
// because of Node's module caching behavior.
export default new Hoot();
<|endoftext|>


<commit_before>'use strict';

angular.module('app')
    .controller('nrgiTransferListCtrl', function (
        $scope,
        $rootScope,
        nrgiAuthSrvc,
        nrgiIdentitySrvc,
        nrgiTransfersSrvc
    ) {
        var limit = 1500,
            currentPage = 0,
            totalPages = 0;

        $scope.count =0;
        $scope.busy = false;

        nrgiTransfersSrvc.query({skip: currentPage*limit, limit: limit}, function (response) {
            $scope.count = response.count;
            $scope.transfers = response.data;
            totalPages = Math.ceil(response.count / limit);
            currentPage = currentPage + 1;
        });

        // $scope.loadMore = function() {
        //     if ($scope.busy) return;
        //     $scope.busy = true;
        //     if(currentPage < totalPages) {
        //         nrgiPaymentsSrvc.query({skip: currentPage*limit, limit: limit}, function (response) {
        //             $scope.projects = _.union($scope.projects, response.data);
        //             currentPage = currentPage + 1;
        //             $scope.busy = false;
        //         });
        //     }
        // };
    });
<commit_msg>SAVEPOINT<commit_after>'use strict';

angular.module('app')
    .controller('nrgiTransferListCtrl', function (
        $scope,
        $rootScope,
        nrgiAuthSrvc,
        nrgiIdentitySrvc,
        nrgiTransfersSrvc
    ) {
        var limit = 1500,
            currentPage = 0,
            totalPages = 0;

        $scope.count =0;
        $scope.busy = false;

        nrgiTransfersSrvc.query({skip: currentPage*limit, limit: limit}, function (response) {
            $scope.count = response.count;
            $scope.transfers = response.data;
            totalPages = Math.ceil(response.count / limit);
            currentPage = currentPage + 1;
        });
        // $scope.loadMore = function() {
        //     if ($scope.busy) return;
        //     $scope.busy = true;
        //     if(currentPage < totalPages) {
        //         nrgiPaymentsSrvc.query({skip: currentPage*limit, limit: limit}, function (response) {
        //             $scope.projects = _.union($scope.projects, response.data);
        //             currentPage = currentPage + 1;
        //             $scope.busy = false;
        //         });
        //     }
        // };
    });
<|endoftext|>


<commit_before>import webpack from 'webpack'
import VueSSRServerPlugin from 'vue-server-renderer/server-plugin'
import nodeExternals from 'webpack-node-externals'
import { each } from 'lodash'
import { resolve } from 'path'
import base from './base.config.js'

/*
|--------------------------------------------------------------------------
| Webpack Server Config
|--------------------------------------------------------------------------
*/
export default function webpackServerConfig () {
  let config = base.call(this, { isServer: true })

  config.name = 'server'

  // env object defined in nuxt.config.js
  let env = {}
  each(this.options.env, (value, key) => {
    env['process.env.' + key] = (typeof value === 'string' ? JSON.stringify(value) : value)
  })

  config = Object.assign(config, {
    target: 'node',
    node: false,
    devtool: 'source-map',
    entry: resolve(this.options.buildDir, 'server.js'),
    output: Object.assign({}, config.output, {
      filename: 'server-bundle.js',
      libraryTarget: 'commonjs2'
    }),
    performance: {
      hints: false
    },
    externals: [
      // https://webpack.js.org/configuration/externals/#externals
      // https://github.com/liady/webpack-node-externals
      nodeExternals({
        // load non-javascript files with extensions, presumably via loaders
        whitelist: [/\.(?!(?:js|json)$).{1,5}$/i]
      })
    ],
    plugins: (config.plugins || []).concat([
      new VueSSRServerPlugin({
        filename: 'server-bundle.json'
      }),
      new webpack.DefinePlugin(Object.assign(env, {
        'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV || (this.options.dev ? 'development' : 'production')),
        'process.env.VUE_ENV': JSON.stringify('server'),
        'process.browser': false,
        'process.server': true
      }))
    ])
  })

  // --------------------------------------
  // Production specific config
  // --------------------------------------
  if (!this.options.dev) {

  }

  // Extend config
  if (typeof this.options.build.extend === 'function') {
    this.options.build.extend.call(this, config, {
      dev: this.options.dev,
      isServer: true
    })
  }

  return config
}
<commit_msg>webpack: don't warn about ssr bundle size<commit_after>import webpack from 'webpack'
import VueSSRServerPlugin from 'vue-server-renderer/server-plugin'
import nodeExternals from 'webpack-node-externals'
import { each } from 'lodash'
import { resolve } from 'path'
import base from './base.config.js'

/*
|--------------------------------------------------------------------------
| Webpack Server Config
|--------------------------------------------------------------------------
*/
export default function webpackServerConfig () {
  let config = base.call(this, { isServer: true })

  config.name = 'server'

  // env object defined in nuxt.config.js
  let env = {}
  each(this.options.env, (value, key) => {
    env['process.env.' + key] = (typeof value === 'string' ? JSON.stringify(value) : value)
  })

  config = Object.assign(config, {
    target: 'node',
    node: false,
    devtool: 'source-map',
    entry: resolve(this.options.buildDir, 'server.js'),
    output: Object.assign({}, config.output, {
      filename: 'server-bundle.js',
      libraryTarget: 'commonjs2'
    }),
    performance: {
      hints: false,
      maxAssetSize: Infinity
    },
    externals: [
      // https://webpack.js.org/configuration/externals/#externals
      // https://github.com/liady/webpack-node-externals
      nodeExternals({
        // load non-javascript files with extensions, presumably via loaders
        whitelist: [/\.(?!(?:js|json)$).{1,5}$/i]
      })
    ],
    plugins: (config.plugins || []).concat([
      new VueSSRServerPlugin({
        filename: 'server-bundle.json'
      }),
      new webpack.DefinePlugin(Object.assign(env, {
        'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV || (this.options.dev ? 'development' : 'production')),
        'process.env.VUE_ENV': JSON.stringify('server'),
        'process.browser': false,
        'process.server': true
      }))
    ])
  })

  // --------------------------------------
  // Production specific config
  // --------------------------------------
  if (!this.options.dev) {

  }

  // Extend config
  if (typeof this.options.build.extend === 'function') {
    this.options.build.extend.call(this, config, {
      dev: this.options.dev,
      isServer: true
    })
  }

  return config
}
<|endoftext|>


<commit_before>const path = require('path');
const EventEmitter = require('events');
const rread = require('readdir-recursive');
const { Collection } = require('discord.js');
const Inhibitor = require('./Inhibitor');
const { InhibitorHandlerEvents } = require('../utils/Constants');

/** @extends EventEmitter */
class InhibitorHandler extends EventEmitter {
    /**
     * Loads Inhibitors and checks messages.
     * @param {Framework} framework - The Akairo framework.
     * @param {Object} options - Options from framework.
     */
    constructor(framework, options = {}){
        super();

        /**
         * The Akairo framework.
         * @readonly
         * @type {Framework}
         */
        this.framework = framework;

        /**
         * Directory to inhibitors.
         * @readonly
         * @type {string}
         */
        this.directory = path.resolve(options.inhibitorDirectory);

        /**
         * Inhibitors loaded, mapped by ID to Inhibitor.
         * @type {Collection.<string, Inhibitor>}
         */
        this.inhibitors = new Collection();

        const filepaths = rread.fileSync(this.directory);
        filepaths.forEach(filepath => {
            this.load(filepath);
        });
    }

    /**
     * Loads an Inhibitor.
     * @param {string} filepath - Path to file.
     * @returns {Inhibitor}
     */
    load(filepath){
        const inhibitor = require(filepath);

        if (!(inhibitor instanceof Inhibitor)) return;
        if (this.inhibitors.has(inhibitor.id)) throw new Error(`Inhibitor ${inhibitor.id} already loaded.`);

        inhibitor.filepath = filepath;
        inhibitor.framework = this.framework;
        inhibitor.client = this.framework.client;
        inhibitor.inhibitorHandler = this;

        this.inhibitors.set(inhibitor.id, inhibitor);
        return inhibitor;
    }

    /**
     * Adds an Inhibitor.
     * @param {string} filename - Filename to lookup in the directory. A .js extension is assumed.
     */
    add(filename){
        const files = rread.fileSync(this.directory);
        const filepath = files.find(file => file.endsWith(`${filename}.js`));

        if (!filepath){
            throw new Error(`File ${filename} not found.`);
        }

        this.emit(InhibitorHandlerEvents.ADD, this.load(filepath));
    }

    /**
     * Removes an Inhibitor.
     * @param {string} id - ID of the Inhibitor.
     */
    remove(id){
        const inhibitor = this.inhibitors.get(id);
        if (!inhibitor) throw new Error(`Inhibitor ${id} does not exist.`);

        delete require.cache[require.resolve(inhibitor.filepath)];
        this.inhibitors.delete(inhibitor.id);

        this.emit(InhibitorHandlerEvents.REMOVE, inhibitor);
    }

    /**
     * Reloads an Inhibitor.
     * @param {string} id - ID of the Inhibitor.
     */
    reload(id){
        const inhibitor = this.inhibitors.get(id);
        if (!inhibitor) throw new Error(`Inhibitor ${id} does not exist.`);

        const filepath = inhibitor.filepath;

        delete require.cache[require.resolve(inhibitor.filepath)];
        this.inhibitors.delete(inhibitor.id);

        this.emit(InhibitorHandlerEvents.RELOAD, this.load(filepath));
    }

    /**
     * Tests the pre-message inhibitors against the message. Rejects with the reason if blocked.
     * @param {Message} message - Message to test.
     * @returns {Promise.<string>}
     */
    testMessage(message){
        return new Promise((resolve, reject) => {
            const promises = this.inhibitors.filter(i => i.type === 'pre' && i.enabled).map(inhibitor => {
                const inhibited = inhibitor.exec(message);

                if (inhibited instanceof Promise) return inhibited.catch(err => {
                    if (err instanceof Error) throw err;
                    throw inhibitor.reason;
                });

                if (!inhibited) return Promise.resolve();
                return Promise.reject(inhibitor.reason);
            });

            Promise.all(promises).then(resolve).catch(errOrReason => {
                if (errOrReason instanceof Error) throw errOrReason;
                reject(errOrReason);
            });
        });
    }

    /**
     * Tests the post-message inhibitors against the message and command. Rejects with the reason if blocked.
     * @param {Message} message - Message to test.
     * @param {Command} command - Command to test.
     * @returns {Promise.<string>}
     */
    testCommand(message, command){
        return new Promise((resolve, reject) => {
            const promises = this.inhibitors.filter(i => i.type === 'post' && i.enabled).map(inhibitor => {
                const inhibited = inhibitor.exec(message, command);

                if (inhibited instanceof Promise) return inhibited.catch(err => {
                    if (err instanceof Error) throw err;
                    throw inhibitor.reason;
                });

                if (!inhibited) return Promise.resolve();
                return Promise.reject(inhibitor.reason);
            });

            Promise.all(promises).then(resolve).catch(errOrReason => {
                if (errOrReason instanceof Error) throw errOrReason;
                reject(errOrReason);
            });
        });
    }
}

module.exports = InhibitorHandler;

/**
 * Emitted when an inhibitor is added.
 * @event InhibitorHandler#add
 * @param {Inhibitor} inhibitor - Inhibitor added.
 */

/**
 * Emitted when an inhibitor is removed.
 * @event InhibitorHandler#remove
 * @param {Inhibitor} inhibitor - Inhibitor removed.
 */

/**
 * Emitted when an inhibitor is reloaded.
 * @event InhibitorHandler#reload
 * @param {Inhibitor} inhibitor - Inhibitor reloaded.
 */
<commit_msg>Better inhibitor handling.<commit_after>const path = require('path');
const EventEmitter = require('events');
const rread = require('readdir-recursive');
const { Collection } = require('discord.js');
const Inhibitor = require('./Inhibitor');
const { InhibitorHandlerEvents } = require('../utils/Constants');

/** @extends EventEmitter */
class InhibitorHandler extends EventEmitter {
    /**
     * Loads Inhibitors and checks messages.
     * @param {Framework} framework - The Akairo framework.
     * @param {Object} options - Options from framework.
     */
    constructor(framework, options = {}){
        super();

        /**
         * The Akairo framework.
         * @readonly
         * @type {Framework}
         */
        this.framework = framework;

        /**
         * Directory to inhibitors.
         * @readonly
         * @type {string}
         */
        this.directory = path.resolve(options.inhibitorDirectory);

        /**
         * Inhibitors loaded, mapped by ID to Inhibitor.
         * @type {Collection.<string, Inhibitor>}
         */
        this.inhibitors = new Collection();

        const filepaths = rread.fileSync(this.directory);
        filepaths.forEach(filepath => {
            this.load(filepath);
        });
    }

    /**
     * Loads an Inhibitor.
     * @param {string} filepath - Path to file.
     * @returns {Inhibitor}
     */
    load(filepath){
        const inhibitor = require(filepath);

        if (!(inhibitor instanceof Inhibitor)) return;
        if (this.inhibitors.has(inhibitor.id)) throw new Error(`Inhibitor ${inhibitor.id} already loaded.`);

        inhibitor.filepath = filepath;
        inhibitor.framework = this.framework;
        inhibitor.client = this.framework.client;
        inhibitor.inhibitorHandler = this;

        this.inhibitors.set(inhibitor.id, inhibitor);
        return inhibitor;
    }

    /**
     * Adds an Inhibitor.
     * @param {string} filename - Filename to lookup in the directory. A .js extension is assumed.
     */
    add(filename){
        const files = rread.fileSync(this.directory);
        const filepath = files.find(file => file.endsWith(`${filename}.js`));

        if (!filepath){
            throw new Error(`File ${filename} not found.`);
        }

        this.emit(InhibitorHandlerEvents.ADD, this.load(filepath));
    }

    /**
     * Removes an Inhibitor.
     * @param {string} id - ID of the Inhibitor.
     */
    remove(id){
        const inhibitor = this.inhibitors.get(id);
        if (!inhibitor) throw new Error(`Inhibitor ${id} does not exist.`);

        delete require.cache[require.resolve(inhibitor.filepath)];
        this.inhibitors.delete(inhibitor.id);

        this.emit(InhibitorHandlerEvents.REMOVE, inhibitor);
    }

    /**
     * Reloads an Inhibitor.
     * @param {string} id - ID of the Inhibitor.
     */
    reload(id){
        const inhibitor = this.inhibitors.get(id);
        if (!inhibitor) throw new Error(`Inhibitor ${id} does not exist.`);

        const filepath = inhibitor.filepath;

        delete require.cache[require.resolve(inhibitor.filepath)];
        this.inhibitors.delete(inhibitor.id);

        this.emit(InhibitorHandlerEvents.RELOAD, this.load(filepath));
    }

    /**
     * Tests the pre-message inhibitors against the message. Rejects with the reason if blocked.
     * @param {Message} message - Message to test.
     * @returns {Promise.<string>}
     */
    testMessage(message){
        const promises = this.inhibitors.filter(i => i.type === 'pre' && i.enabled).map(inhibitor => {
            const inhibited = Promise.resolve(inhibitor.exec(message));
            return inhibited;
        });

        return Promise.all(promises).catch(errOrReason => {
            if (errOrReason instanceof Error) throw errOrReason;
            return errOrReason;
        });
    }

    /**
     * Tests the post-message inhibitors against the message and command. Rejects with the reason if blocked.
     * @param {Message} message - Message to test.
     * @param {Command} command - Command to test.
     * @returns {Promise.<string>}
     */
    testCommand(message, command){
        const promises = this.inhibitors.filter(i => i.type === 'pre' && i.enabled).map(inhibitor => {
            const inhibited = Promise.resolve(inhibitor.exec(message, command));
            return inhibited;
        });

        return Promise.all(promises).catch(errOrReason => {
            if (errOrReason instanceof Error) throw errOrReason;
            return errOrReason;
        });
    }
}

module.exports = InhibitorHandler;

/**
 * Emitted when an inhibitor is added.
 * @event InhibitorHandler#add
 * @param {Inhibitor} inhibitor - Inhibitor added.
 */

/**
 * Emitted when an inhibitor is removed.
 * @event InhibitorHandler#remove
 * @param {Inhibitor} inhibitor - Inhibitor removed.
 */

/**
 * Emitted when an inhibitor is reloaded.
 * @event InhibitorHandler#reload
 * @param {Inhibitor} inhibitor - Inhibitor reloaded.
 */
<|endoftext|>


<commit_before>require('./css/styles.css');
import ThemeProvider from './components/ThemeProvider';
import LayoutContainer from './components/LayoutContainer';
import LayoutItem from './components/LayoutItem';
import Button from 'material-ui/Button';
export {
  ThemeProvider,
  LayoutContainer,
  LayoutItem,
  Button
};<commit_msg>Fix import sorting<commit_after>import ThemeProvider from './components/ThemeProvider';
import LayoutContainer from './components/LayoutContainer';
import LayoutItem from './components/LayoutItem';
import Button from 'material-ui/Button';
require('./css/styles.css');
export {
  ThemeProvider,
  LayoutContainer,
  LayoutItem,
  Button
};<|endoftext|>


<commit_before>// @flow

import qs from 'query-string'

import {IS_PRODUCTION} from '@frogpond/constants'

let root: string

export function setApiRoot(url: string) {
	root = url
}

export const API = (path: string, query: ?Object = null) => {
	if (!IS_PRODUCTION) {
		if (!path.startsWith('/')) {
			throw new Error('invalid path requested from the api!')
		}
	}

	let url = root + path

	if (query) {
		url += `?${qs.stringify(query)}`
	}

	return url
}
<commit_msg>m/api: Don't use Object explicitly<commit_after>// @flow

import qs from 'query-string'

import {IS_PRODUCTION} from '@frogpond/constants'

let root: string

export function setApiRoot(url: string) {
	root = url
}

export const API = (path: string, query: ?unknown = null) => {
	if (!IS_PRODUCTION) {
		if (!path.startsWith('/')) {
			throw new Error('invalid path requested from the api!')
		}
	}

	let url = root + path

	if (query) {
		url += `?${qs.stringify(query)}`
	}

	return url
}
<|endoftext|>


<commit_before>// use strict
'use strict';

// require dependencies
const config     = require ('config');
const sitemap    = require ('sitemap');
const controller = require ('controller');

/**
 * build meta controller
 */
class metaController extends controller {
  /**
   * construct example controller class
   */
  constructor () {
    // run super eden
    super ();

    // bind private variables
    this._sitemap = null;

    // bind private methods
    this._compile    = this._compile.bind (this);
    this._generate   = this._generate.bind (this);
    this._middleware = this._middleware.bind (this);

    // run middleware
    this.eden.router.use (this._middleware);

    // on render
    this.eden.pre ('view.compile', this._compile);

    // generate sitemap
    if (config.get ('sitemap') && config.get ('sitemap.enabled')) {
      // generate on interval
      this._generate ();

      // set interval
      if (config.get ('sitemap.interval')) setInterval (this._generate, config.get ('sitemap.interval'));
    }
  }

  /**
   * create sitemap
   *
   * @param  {Request}   req
   * @param  {Response}  res
   *
   * @route  {get} /sitemap.xml
   *
   * @return {Promise}
   */
  async sitemap (req, res, next) {
    // check sitemap
    if (!this._sitemap) return next ();

    // return sitemap
    this._sitemap.toXML ((err, xml) => {
      // check error
      if (err) return next ();

      // set header
      res.header ('Content-Type', 'application/xml');

      // send xml
      res.send (xml);
    });
  }

  /**
   * generates xml
   *
   * @return {Promise}
   */
  async _generate () {
    // create sitemap
    let map = {
      'hostname'  : 'https://' + config.get ('domain'),
      'cacheTime' : config.get ('sitemap.cache') || 600000,
      'urls'      : [
        {
          'url'        : '',
          'priority'   : 1,
          'changefreq' : 'daily'
        }
      ]
    };

    // hook generate
    await this.eden.hook ('sitemap', map);

    // create
    this._sitemap = sitemap.createSitemap (map);

    // add to eden
    this.eden.sitemap = this._sitemap;

    // trigger sitemap
    this.eden.emit ('sitemap');
  }

  /**
   * compile view
   *
   * @param  {Object}  render
   *
   * @return {Promise}
   */
  async _compile (render) {
    // check meta
    let meta = render.state.meta || {};

    // delete meta
    delete render.state.meta;

    // create head
    if (!render.page.head) render.page.head = '';

    // loop meta
    for (let type in meta) {
      // loop for name
      for (let name in meta[type]) {
        // add to head
        render.page.head += '<' + type;

        // loop for tags
        for (let tag in meta[type][name]) {
          // add tag
          render.page.head += ' ' + tag + '="' + meta[type][name][tag] + '"';
        }

        // close tag
        render.page.head += ' />';
      }
    }
  }

  /**
   * build middleware
   *
   * @param  {request}  req Express request
   * @param  {response} res Express response
   * @param  {function} next Next callback
   *
   * @private
   */
  async _middleware (req, res, next) {
    // create meta function
    req.meta = res.meta = (type, opts) => {
      // check type and opts
      if (typeof type === 'object' || !opts) {
        // set variables
        opts = type;
        type = 'meta';
      }

      // get name
      let name = opts.name || opts.property || opts[Object.keys (opts)[0]];

      // check type
      if (type === 'title')       return req.title (opts);
      if (type === 'image')       return req.image (opts);
      if (type === 'description') return req.description (opts);

      // check opts
      if (typeof opts === 'string') {
        opts = {
          'name'    : type,
          'content' : opts
        };
        type = 'meta';
      }

      // check if meta exists
      if (!res.locals.meta) res.locals.meta = {};

      // check meta type
      if (!res.locals.meta[type]) res.locals.meta[type] = {};

      // set meta
      res.locals.meta[type][name] = opts;

      // return req
      return res;
    };

    // create description
    req.og = res.og = (name, content) => {
      // add description
      req.meta ({
        'property' : 'og:' + name,
        'content'  : content
      });

      // return req
      return res;
    };

    // create description
    req.twitter = res.twitter = (name, content) => {
      // add description
      req.meta ({
        'name'    : 'twitter:' + name,
        'content' : content
      });

      // return req
      return res;
    };

    // create description
    req.title = res.title = (title) => {
      // set title
      res.locals.page = res.locals.page || {};

      // set title
      res.locals.page.title = title;

      // add description
      req.og ('title', title);
      req.twitter ('title', title);

      // return req
      return res;
    };

    // create description
    req.description = res.description = (description) => {
      // add description
      req.og ('description', description);
      req.twitter ('description', description);

      // return req
      return res;
    };

    // create description
    req.image = res.image = (url) => {
      // add image
      req.og ('image', url);
      req.twitter ('image', url);

      // return req
      return res;
    };

    // set default title
    req.title (config.get ('title'));

    // set default
    req.og ('url', '//' + config.get ('domain') + req.url);
    req.og ('locale', req.language);
    req.twitter ('card', 'summary');
    req.twitter ('site', config.get ('title'));

    // run next
    next ();
  }
}

/**
 * export meta controller
 *
 * @type {metaController}
 */
exports = module.exports = metaController;
<commit_msg>amend meta bundle for description<commit_after>// use strict
'use strict';

// require dependencies
const config     = require ('config');
const sitemap    = require ('sitemap');
const controller = require ('controller');

/**
 * build meta controller
 */
class metaController extends controller {
  /**
   * construct example controller class
   */
  constructor () {
    // run super eden
    super ();

    // bind private variables
    this._sitemap = null;

    // bind private methods
    this._compile    = this._compile.bind (this);
    this._generate   = this._generate.bind (this);
    this._middleware = this._middleware.bind (this);

    // run middleware
    this.eden.router.use (this._middleware);

    // on render
    this.eden.pre ('view.compile', this._compile);

    // generate sitemap
    if (config.get ('sitemap') && config.get ('sitemap.enabled')) {
      // generate on interval
      this._generate ();

      // set interval
      if (config.get ('sitemap.interval')) setInterval (this._generate, config.get ('sitemap.interval'));
    }
  }

  /**
   * create sitemap
   *
   * @param  {Request}   req
   * @param  {Response}  res
   *
   * @route  {get} /sitemap.xml
   *
   * @return {Promise}
   */
  async sitemap (req, res, next) {
    // check sitemap
    if (!this._sitemap) return next ();

    // return sitemap
    this._sitemap.toXML ((err, xml) => {
      // check error
      if (err) return next ();

      // set header
      res.header ('Content-Type', 'application/xml');

      // send xml
      res.send (xml);
    });
  }

  /**
   * generates xml
   *
   * @return {Promise}
   */
  async _generate () {
    // create sitemap
    let map = {
      'hostname'  : 'https://' + config.get ('domain'),
      'cacheTime' : config.get ('sitemap.cache') || 600000,
      'urls'      : [
        {
          'url'        : '',
          'priority'   : 1,
          'changefreq' : 'daily'
        }
      ]
    };

    // hook generate
    await this.eden.hook ('sitemap', map);

    // create
    this._sitemap = sitemap.createSitemap (map);

    // add to eden
    this.eden.sitemap = this._sitemap;

    // trigger sitemap
    this.eden.emit ('sitemap');
  }

  /**
   * compile view
   *
   * @param  {Object}  render
   *
   * @return {Promise}
   */
  async _compile (render) {
    // check meta
    let meta = render.state.meta || {};

    // delete meta
    delete render.state.meta;

    // create head
    if (!render.page.head) render.page.head = '';

    // loop meta
    for (let type in meta) {
      // loop for name
      for (let name in meta[type]) {
        // add to head
        render.page.head += '<' + type;

        // loop for tags
        for (let tag in meta[type][name]) {
          // add tag
          render.page.head += ' ' + tag + '="' + meta[type][name][tag] + '"';
        }

        // close tag
        render.page.head += ' />';
      }
    }
  }

  /**
   * build middleware
   *
   * @param  {request}  req Express request
   * @param  {response} res Express response
   * @param  {function} next Next callback
   *
   * @private
   */
  async _middleware (req, res, next) {
    // create meta function
    req.meta = res.meta = (type, opts) => {
      // check type and opts
      if (typeof type === 'object' || !opts) {
        // set variables
        opts = type;
        type = 'meta';
      }

      // get name
      let name = opts.name || opts.property || opts[Object.keys (opts)[0]];

      // check type
      if (type === 'title')       return req.title (opts);
      if (type === 'image')       return req.image (opts);
      if (type === 'description') return req.description (opts);

      // check opts
      if (typeof opts === 'string') {
        opts = {
          'name'    : type,
          'content' : opts
        };
        type = 'meta';
      }

      // check if meta exists
      if (!res.locals.meta) res.locals.meta = {};

      // check meta type
      if (!res.locals.meta[type]) res.locals.meta[type] = {};

      // set meta
      res.locals.meta[type][name] = opts;

      // return req
      return res;
    };

    // create description
    req.og = res.og = (name, content) => {
      // add description
      req.meta ({
        'property' : 'og:' + name,
        'content'  : content
      });

      // return req
      return res;
    };

    // create description
    req.twitter = res.twitter = (name, content) => {
      // add description
      req.meta ({
        'name'    : 'twitter:' + name,
        'content' : content
      });

      // return req
      return res;
    };

    // create description
    req.title = res.title = (title) => {
      // set title
      res.locals.page = res.locals.page || {};

      // set title
      res.locals.page.title = title;

      // add description
      req.og ('title', title);
      req.twitter ('title', title);

      // return req
      return res;
    };

    // create description
    req.description = res.description = (description) => {
      // add description
      req.og ('description', description);
      req.meta ('description', description);
      req.twitter ('description', description);

      // return req
      return res;
    };

    // create description
    req.image = res.image = (url) => {
      // add image
      req.og ('image', url);
      req.twitter ('image', url);

      // return req
      return res;
    };

    // set default title
    req.title (config.get ('title'));

    // set default
    req.og ('url', '//' + config.get ('domain') + req.url);
    req.og ('locale', req.language);
    req.twitter ('card', 'summary');
    req.twitter ('site', config.get ('title'));

    // run next
    next ();
  }
}

/**
 * export meta controller
 *
 * @type {metaController}
 */
exports = module.exports = metaController;
<|endoftext|>


<commit_before>/* global describe, it, beforeEach, afterEach */
/* jshint expr:true */

var expect = require('chai').expect;
var request = require('request');
var server = require('../server').server;
var findNewNick = require("../server").findNewNick;

var connection;

describe("Server", function() {
  describe("presence", function() {

    function signin(nick, callback) {
      request.post('http://localhost:3000/signin',
                   {form: {nick: nick}},
                   callback);
    }

    function signout(nick, callback) {
      request.post('http://localhost:3000/signout',
                   {form: {nick: nick}},
                   callback);
    }

    beforeEach(function() {
      connection = server.listen(3000);
    });

    afterEach(function() {
      connection.close();
    });

    it("should have no users logged in", function() {
      expect(server.get("users")).to.be.empty;
    });

    it('should have foo logged in', function(done) {
      signin('foo', function() {
        expect(server.get('users')).to.eql(['foo']);
        done();
      });
    });

    it('should have no users logged in', function(done) {
      signin('foo', function() {
        signout('foo', function() {
          expect(server.get('users')).to.be.empty;
          done();
        });
      });
    });

    it("should return the user's nick", function(done) {
      var nick1 = "foo";
      request.post("http://localhost:3000/signin", {form: {nick: nick1}},
                   function(err, res, body) {
        var data = JSON.parse(body);
        expect(data.nick).to.eql(nick1);
        expect(data.users).to.be.empty;
        done();
      });
    });

    it("should fix the user's nick if it already exists", function(done) {
      var nick1 = "foo";
      /* jshint unused: false */
      request.post("http://localhost:3000/signin", {form: {nick: nick1}},
                   function(err, res, body) {
        request.post("http://localhost:3000/signin", {form: {nick: nick1}},
                   function(err, res, body) {
          expect(JSON.parse(body).nick).to.eql(findNewNick(nick1));
          done();
        });
      });
    });

    it("should preserve existing chars of the nick when finding a new one",
       function() {
      var testNicks = {
        "foo": "foo1",
        "foo1": "foo2",
        "foo10": "foo11",
        "foo0": "foo1",
        "foo01": "foo02",
        "foo09": "foo10",

        // Now put a number in the "first part".
        "fo1o": "fo1o1",
        "fo1o1": "fo1o2",
        "fo1o10": "fo1o11",
        "fo1o0": "fo1o1",
        "fo1o01": "fo1o02",
        "fo1o09": "fo1o10"
      };
      for (var nick in testNicks)
        expect(findNewNick(nick)).to.equal(testNicks[nick]);
    });

    it("should return existing users", function(done) {
      var nick1 = "foo";
      var nick2 = "bar";
      request.post("http://localhost:3000/signin", {form: {nick: nick1}},
                   function() {
          request.post("http://localhost:3000/signin", {form: {nick: nick2}},
                   function(err, res, body) {
            var data = JSON.parse(body);
            expect(data.nick).to.eql(nick2);
            expect(data.users).to.eql([nick1]);
            done();
          });
        });
    });
  });
});
<commit_msg>Fix issues found by jshint<commit_after>/* global describe, it, beforeEach, afterEach */
/* jshint expr:true */

var expect = require('chai').expect;
var request = require('request');
var server = require('../server').server;
var findNewNick = require("../server").findNewNick;

var connection;

describe("Server", function() {
  describe("presence", function() {

    function signin(nick, callback) {
      request.post('http://localhost:3000/signin',
                   {form: {nick: nick}},
                   callback);
    }

    function signout(nick, callback) {
      request.post('http://localhost:3000/signout',
                   {form: {nick: nick}},
                   callback);
    }

    beforeEach(function() {
      connection = server.listen(3000);
    });

    afterEach(function() {
      connection.close();
    });

    it("should have no users logged in", function() {
      expect(server.get("users")).to.be.empty;
    });

    it('should have foo logged in', function(done) {
      signin('foo', function() {
        expect(server.get('users')).to.eql(['foo']);
        done();
      });
    });

    it('should have no users logged in after logging in and out',
    function(done) {
      signin('foo', function() {
        signout('foo', function() {
          expect(server.get('users')).to.be.empty;
          done();
        });
      });
    });

    it("should return the user's nick", function(done) {
      var nick1 = "foo";
      request.post("http://localhost:3000/signin", {form: {nick: nick1}},
                   function(err, res, body) {
        var data = JSON.parse(body);
        expect(data.nick).to.eql(nick1);
        expect(data.users).to.be.empty;
        done();
      });
    });

    it("should fix the user's nick if it already exists", function(done) {
      var nick1 = "foo";
      /* jshint unused: false */
      request.post("http://localhost:3000/signin", {form: {nick: nick1}},
                   function(err, res, body) {
        request.post("http://localhost:3000/signin", {form: {nick: nick1}},
                   function(err, res, body) {
          expect(JSON.parse(body).nick).to.eql(findNewNick(nick1));
          done();
        });
      });
    });

    it("should preserve existing chars of the nick when finding a new one",
       function() {
      var testNicks = {
        "foo": "foo1",
        "foo1": "foo2",
        "foo10": "foo11",
        "foo0": "foo1",
        "foo01": "foo02",
        "foo09": "foo10",

        // Now put a number in the "first part".
        "fo1o": "fo1o1",
        "fo1o1": "fo1o2",
        "fo1o10": "fo1o11",
        "fo1o0": "fo1o1",
        "fo1o01": "fo1o02",
        "fo1o09": "fo1o10"
      };
      for (var nick in testNicks)
        expect(findNewNick(nick)).to.equal(testNicks[nick]);
    });

    it("should return existing users", function(done) {
      var nick1 = "foo";
      var nick2 = "bar";
      request.post("http://localhost:3000/signin", {form: {nick: nick1}},
                   function() {
          request.post("http://localhost:3000/signin", {form: {nick: nick2}},
                   function(err, res, body) {
            var data = JSON.parse(body);
            expect(data.nick).to.eql(nick2);
            expect(data.users).to.eql([nick1]);
            done();
          });
        });
    });
  });
});
<|endoftext|>


<commit_before>/* eslint-disable no-tabs */
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import ClickObserver from '@ckeditor/ckeditor5-engine/src/view/observer/clickobserver';
import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';
import clickOutsideHandler from '@ckeditor/ckeditor5-ui/src/bindings/clickoutsidehandler';
import uid from '@ckeditor/ckeditor5-utils/src/uid';
import global from '@ckeditor/ckeditor5-utils/src/dom/global';

import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import MainFormView from './ui/mainformview';

// Need math commands from there
import MathEditing from './mathediting';
import { defaultConfig, getSelectedMathModelWidget, resizeInputElement } from './utils';

import mathIcon from './math.svg';

const mathKeystroke = 'Ctrl+4';
const mathDisplayKeystroke = 'Ctrl+M';

export default class MathUI extends Plugin {
	static get requires() {
		return [ ContextualBalloon, MathEditing ];
	}

	static get pluginName() {
		return 'MathUI';
	}

	init() {
		const editor = this.editor;
		editor.editing.view.addObserver( ClickObserver );

		this._previewUid = `math-preview-${ uid() }`;

		this.formView = this._createFormView();

		this._balloon = editor.plugins.get( ContextualBalloon );

		this._createToolbarMathButton();

		this._enableUserBalloonInteractions();
	}

	destroy() {
		super.destroy();

		this.formView.destroy();

		// Destroy preview element
		const prewviewEl = global.document.getElementById( this._previewUid );
		if ( prewviewEl ) {
			prewviewEl.parentNode.removeChild( prewviewEl );
		}
	}

	_showUI() {
		const editor = this.editor;
		const mathCommand = editor.commands.get( 'math' );

		if ( !mathCommand.isEnabled ) {
			return;
		}

		this._addFormView();

		this._balloon.showStack( 'main' );
	}

	_createFormView() {
		const editor = this.editor;
		const mathCommand = editor.commands.get( 'math' );

		const mathConfig = Object.assign( defaultConfig, this.editor.config.get( 'math' ) );

		const formView = new MainFormView( editor.locale, mathConfig.engine, mathConfig.enablePreview, this._previewUid );

		formView.mathInputView.bind( 'value' ).to( mathCommand, 'value' );

		// Form elements should be read-only when corresponding commands are disabled.
		formView.mathInputView.bind( 'isReadOnly' ).to( mathCommand, 'isEnabled', value => !value );

		// Listen to submit button click
		this.listenTo( formView, 'submit', () => {
			editor.execute( 'math', formView.equation, mathCommand.display, mathConfig.outputType, mathConfig.forceOutputType );
			this._closeFormView();
		} );

		// Listen to cancel button click
		this.listenTo( formView, 'cancel', () => {
			this._closeFormView();
		} );

		// Close plugin ui, if esc is pressed (while ui is focused)
		formView.keystrokes.set( 'esc', ( data, cancel ) => {
			this.formView.fire( 'submit' );
			this._closeFormView();
		} );

		formView.keystrokes.set( 'Enter', ( data, cancel ) => {
			this.formView.fire( 'submit' );
			this._closeFormView();
		} );

		this.listenTo( formView, 'updatedMath', () => {
			this._balloon.updatePosition( this._getBalloonPositionData() );
		} );

		return formView;
	}

	_addFormView() {
		if ( this._isFormInPanel ) {
			return;
		}

		const editor = this.editor;
		const mathCommand = editor.commands.get( 'math' );

		this._balloon.add( {
			view: this.formView,
			position: this._getBalloonPositionData(),
			balloonClassName: 'ck-math-balloon'
		} );

		if ( this._balloon.visibleView === this.formView ) {
			this.formView.mathInputView.select();
		}

		this.formView.equation = mathCommand.value || '';

		// After updating the equation, make sure to resize the input element
		if ( this.formView.mathInputView.element ) {
			resizeInputElement( this.formView.mathInputView.element );
		}
	}

	_hideUI() {
		if ( !this._isFormInPanel ) {
			return;
		}

		const editor = this.editor;

		this.stopListening( editor.ui, 'update' );
		this.stopListening( this._balloon, 'change:visibleView' );

		editor.editing.view.focus();

		// Remove form first because it's on top of the stack.
		this._removeFormView();
	}

	_closeFormView() {
		const mathCommand = this.editor.commands.get( 'math' );
		if ( mathCommand.value !== undefined ) {
			this._removeFormView();
		} else {
			this._hideUI();
		}
	}

	_removeFormView() {
		if ( this._isFormInPanel ) {
			this._balloon.remove( this.formView );

			// Hide preview element
			const prewviewEl = global.document.getElementById( this._previewUid );
			if ( prewviewEl ) {
				prewviewEl.style.visibility = 'hidden';
			}

			this.editor.editing.view.focus();
		}
	}

	_getBalloonPositionData() {
		const view = this.editor.editing.view;
		const viewDocument = view.document;
		const target = view.domConverter.viewRangeToDom( viewDocument.selection.getFirstRange() );
		return { target };
	}

	_createToolbarMathButton() {
		const editor = this.editor;
		const mathCommand = editor.commands.get( 'math' );
		const t = editor.t;

		// Handle the `Ctrl+4` keystroke and show the panel.
		editor.keystrokes.set( mathKeystroke, ( keyEvtData, cancel ) => {
			cancel();

			if ( mathCommand.isEnabled ) {
				this._showUI();
			}
		} );

		// Handle the `Ctrl+M` keystroke and show the panel.
		editor.keystrokes.set( mathDisplayKeystroke, ( keyEvtData, cancel ) => {
			// Prevent focusing the search bar in FF and opening new tab in Edge. #153, #154.
			cancel();

			if ( mathCommand.isEnabled ) {
				mathCommand.display = true;
				this._showUI();
			}
		} );

		this.editor.ui.componentFactory.add( 'math', locale => {
			const button = new ButtonView( locale );

			button.isEnabled = true;
			button.label = t( 'Insert math' );
			button.icon = mathIcon;
			button.keystroke = mathKeystroke;
			button.tooltip = true;
			button.isToggleable = true;

			button.bind( 'isEnabled' ).to( mathCommand, 'isEnabled' );

			this.listenTo( button, 'execute', () => this._showUI() );

			return button;
		} );

		this.editor.ui.componentFactory.add( 'mathDisplay', locale => {
			const button = new ButtonView( locale );

			button.isEnabled = true;
			button.label = t( 'Insert display math block' );
			button.icon = mathIcon;
			button.keystroke = mathDisplayKeystroke;
			button.tooltip = true;
			button.isToggleable = true;

			button.bind( 'isEnabled' ).to( mathCommand, 'isEnabled' );

			this.listenTo( button, 'execute', () => {
				mathCommand.display = true;
				this._showUI();
			} );

			return button;
		} );
	}

	_getSelectedLaTeXElement() {
		const selection = this.editor.model.document.selection;
		return getSelectedMathModelWidget( selection, true );
	}

	_enableUserBalloonInteractions() {
		const viewDocument = this.editor.editing.view.document;

		// Handle click on view document and show panel when selection is placed inside the latex element
		this.listenTo( viewDocument, 'click', () => {
			const selectedElement = this._getSelectedLaTeXElement();
			if ( selectedElement ) {
				// Then show panel but keep focus inside editor editable.
				this._showUI();
			}
		} );

		// Close on click outside of balloon panel element.
		clickOutsideHandler( {
			emitter: this.formView,
			activator: () => this._isFormInPanel,
			contextElements: [ this._balloon.view.element ],
			callback: () => this.formView.fire( 'submit' )
		} );
	}

	get _isUIVisible() {
		const visibleView = this._balloon.visibleView;

		return visibleView == this.formView;
	}

	get _isFormInPanel() {
		return this._balloon.hasView( this.formView );
	}
}
<commit_msg>Fix really minor MathJax bug<commit_after>/* eslint-disable no-tabs */
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import ClickObserver from '@ckeditor/ckeditor5-engine/src/view/observer/clickobserver';
import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';
import clickOutsideHandler from '@ckeditor/ckeditor5-ui/src/bindings/clickoutsidehandler';
import uid from '@ckeditor/ckeditor5-utils/src/uid';
import global from '@ckeditor/ckeditor5-utils/src/dom/global';

import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import MainFormView from './ui/mainformview';

// Need math commands from there
import MathEditing from './mathediting';
import { defaultConfig, getSelectedMathModelWidget, resizeInputElement } from './utils';

import mathIcon from './math.svg';

const mathKeystroke = 'Ctrl+4';
const mathDisplayKeystroke = 'Ctrl+M';

export default class MathUI extends Plugin {
	static get requires() {
		return [ ContextualBalloon, MathEditing ];
	}

	static get pluginName() {
		return 'MathUI';
	}

	init() {
		const editor = this.editor;
		editor.editing.view.addObserver( ClickObserver );

		this._previewUid = `math-preview-${ uid() }`;

		this.formView = this._createFormView();

		this._balloon = editor.plugins.get( ContextualBalloon );

		this._createToolbarMathButton();

		this._enableUserBalloonInteractions();
	}

	destroy() {
		super.destroy();

		this.formView.destroy();

		// Destroy preview element
		const prewviewEl = global.document.getElementById( this._previewUid );
		if ( prewviewEl ) {
			prewviewEl.parentNode.removeChild( prewviewEl );
		}
	}

	_showUI() {
		const editor = this.editor;
		const mathCommand = editor.commands.get( 'math' );

		if ( !mathCommand.isEnabled ) {
			return;
		}

		this._addFormView();

		this._balloon.showStack( 'main' );
	}

	_createFormView() {
		const editor = this.editor;
		const mathCommand = editor.commands.get( 'math' );

		const mathConfig = Object.assign( defaultConfig, this.editor.config.get( 'math' ) );

		const formView = new MainFormView( editor.locale, mathConfig.engine, mathConfig.enablePreview, this._previewUid );

		formView.mathInputView.bind( 'value' ).to( mathCommand, 'value' );

		// Form elements should be read-only when corresponding commands are disabled.
		formView.mathInputView.bind( 'isReadOnly' ).to( mathCommand, 'isEnabled', value => !value );

		// Listen to submit button click
		this.listenTo( formView, 'submit', () => {
			editor.execute( 'math', formView.equation, mathCommand.display, mathConfig.outputType, mathConfig.forceOutputType );
			this._closeFormView();
		} );

		// Listen to cancel button click
		this.listenTo( formView, 'cancel', () => {
			this._closeFormView();
		} );

		// Close plugin ui, if esc is pressed (while ui is focused)
		formView.keystrokes.set( 'esc', ( data, cancel ) => {
			this.formView.fire( 'submit' );
			this._closeFormView();
		} );

		formView.keystrokes.set( 'Enter', ( data, cancel ) => {
			this.formView.fire( 'submit' );
			this._closeFormView();
		} );

		this.listenTo( formView, 'updatedMath', () => {
			this._balloon.updatePosition( this._getBalloonPositionData() );
		} );

		return formView;
	}

	_addFormView() {
		if ( this._isFormInPanel ) {
			return;
		}

		const editor = this.editor;
		const mathCommand = editor.commands.get( 'math' );

		this._balloon.add( {
			view: this.formView,
			position: this._getBalloonPositionData(),
			balloonClassName: 'ck-math-balloon'
		} );

		if ( this._balloon.visibleView === this.formView ) {
			this.formView.mathInputView.select();
		}

		this.formView.equation = mathCommand.value || '';

		// After updating the equation, make sure to resize the input element
		if ( this.formView.mathInputView.element ) {
			resizeInputElement( this.formView.mathInputView.element );
		}
	}

	_hideUI() {
		if ( !this._isFormInPanel ) {
			return;
		}

		const editor = this.editor;

		this.stopListening( editor.ui, 'update' );
		this.stopListening( this._balloon, 'change:visibleView' );

		editor.editing.view.focus();

		// Remove form first because it's on top of the stack.
		this._removeFormView();
	}

	_closeFormView() {
		const mathCommand = this.editor.commands.get( 'math' );
		if ( mathCommand.value !== undefined ) {
			this._removeFormView();
		} else {
			this._hideUI();
		}
	}

	_removeFormView() {
		if ( this._isFormInPanel ) {
			this._balloon.remove( this.formView );

			// Hide preview element
			const prewviewEl = global.document.getElementById( this._previewUid );
			if ( prewviewEl ) {
				prewviewEl.style.visibility = 'hidden';
			}

			this.editor.editing.view.focus();
		}
	}

	_getBalloonPositionData() {
		const view = this.editor.editing.view;
		const viewDocument = view.document;
		const target = view.domConverter.viewRangeToDom( viewDocument.selection.getFirstRange() );
		return { target };
	}

	_createToolbarMathButton() {
		const editor = this.editor;
		const mathCommand = editor.commands.get( 'math' );
		const t = editor.t;

		// Handle the `Ctrl+4` keystroke and show the panel.
		editor.keystrokes.set( mathKeystroke, ( keyEvtData, cancel ) => {
			cancel();

			if ( mathCommand.isEnabled ) {
				this._showUI();
			}
		} );

		// Handle the `Ctrl+M` keystroke and show the panel.
		editor.keystrokes.set( mathDisplayKeystroke, ( keyEvtData, cancel ) => {
			// Prevent focusing the search bar in FF and opening new tab in Edge. #153, #154.
			cancel();

			if ( mathCommand.isEnabled ) {
				mathCommand.display = true;
				this._showUI();
			}
		} );

		this.editor.ui.componentFactory.add( 'math', locale => {
			const button = new ButtonView( locale );

			button.isEnabled = true;
			button.label = t( 'Insert math' );
			button.icon = mathIcon;
			button.keystroke = mathKeystroke;
			button.tooltip = true;
			button.isToggleable = true;

			button.bind( 'isEnabled' ).to( mathCommand, 'isEnabled' );

			this.listenTo( button, 'execute', () => this._showUI() );

			return button;
		} );

		this.editor.ui.componentFactory.add( 'mathDisplay', locale => {
			const button = new ButtonView( locale );

			button.isEnabled = true;
			button.label = t( 'Insert display math block' );
			button.icon = mathIcon;
			button.keystroke = mathDisplayKeystroke;
			button.tooltip = true;
			button.isToggleable = true;

			button.bind( 'isEnabled' ).to( mathCommand, 'isEnabled' );

			this.listenTo( button, 'execute', () => {
				mathCommand.display = true;
				this._showUI();
			} );

			return button;
		} );
	}

	_getSelectedLaTeXElement() {
		const selection = this.editor.model.document.selection;
		return getSelectedMathModelWidget( selection );
	}

	_enableUserBalloonInteractions() {
		const viewDocument = this.editor.editing.view.document;

		// Handle click on view document and show panel when selection is placed inside the latex element
		this.listenTo( viewDocument, 'click', () => {
			const selectedElement = this._getSelectedLaTeXElement();
			if ( selectedElement ) {
				// Then show panel but keep focus inside editor editable.
				this._showUI();
			}
		} );

		// Close on click outside of balloon panel element.
		clickOutsideHandler( {
			emitter: this.formView,
			activator: () => this._isFormInPanel,
			contextElements: [ this._balloon.view.element ],
			callback: () => this.formView.fire( 'submit' )
		} );
	}

	get _isUIVisible() {
		const visibleView = this._balloon.visibleView;

		return visibleView == this.formView;
	}

	get _isFormInPanel() {
		return this._balloon.hasView( this.formView );
	}
}
<|endoftext|>


<commit_before>"use strict";

const SyncStrategy = require("./SyncStrategy");

const defaults = {
    syncsBufferLength: 5,
    RTTEstimate: 2,       // estimate the RTT as two steps (for updateRate=6, that's 200ms)
    extrapolate: 2,       // player performs method "X" which means extrapolate to match server time. that 100 + (0..100)
    bending: 0.6          // amount of bending towards position of sync object
};

class ExtrapolateStrategy extends SyncStrategy {

    constructor(clientEngine, inputOptions) {

        const options = Object.assign({}, defaults, inputOptions);
        super(clientEngine, options);

        this.newSync = null;
        this.recentInputs = {};
        this.gameEngine = this.clientEngine.gameEngine;
        this.gameEngine.on('postStep', this.extrapolate.bind(this));
        this.gameEngine.on('client.syncReceived', this.collectSync.bind(this));
        this.gameEngine.on('client.preInput', this.clientInputSave.bind(this));
    }

    // keep a buffer of inputs so that we can replay them on extrapolation
    clientInputSave(inputData) {

        // if no inputs have been stored for this step, create an array
        if (!this.recentInputs[inputData.step]) {
            this.recentInputs[inputData.step] = [];
        }
        this.recentInputs[inputData.step].push(inputData);
    }

    // collect a sync and its events
    collectSync(e) {
        // TODO avoid editing the input event

        // keep a reference of events by object id
        e.syncObjects = {};
        e.syncEvents.forEach(sEvent => {
            let o = sEvent.objectInstance;
            if (!e.syncObjects[o.id]) {
                e.syncObjects[o.id] = [];
            }
            e.syncObjects[o.id].push(sEvent);
        });

        // keep a reference of events by step
        e.syncSteps = {};
        e.syncEvents.forEach(sEvent => {

            // add an entry for this step and event-name
            if (!e.syncSteps[sEvent.stepCount]) e.syncSteps[sEvent.stepCount] = {};
            if (!e.syncSteps[sEvent.stepCount][sEvent.eventName]) e.syncSteps[sEvent.stepCount][sEvent.eventName] = [];
            e.syncSteps[sEvent.stepCount][sEvent.eventName].push(sEvent);
        });

        // remember this sync
        this.newSync = e;
    }

    // add an object to our world
    addNewObject(objId, newObj) {

        let curObj = newObj.class.newFrom(newObj);
        this.gameEngine.addObjectToWorld(curObj);
        curObj.initRenderObject(this.gameEngine.renderer);
        console.log(`adding new object ${curObj}`);

        // if this game keeps a physics engine on the client side,
        // we need to update it as well
        if (this.gameEngine.physicsEngine) {
            curObj.initPhysicsObject(this.gameEngine.physicsEngine);
        }

        return curObj;
    }

    // clean up the input buffer
    cleanRecentInputs() {
        let firstReplayStep = this.gameEngine.world.stepCount - this.options.extrapolate;
        for (let input of Object.keys(this.recentInputs)) {
            if (this.recentInputs[input].step < firstReplayStep) {
                delete this.recentInputs[input];
            }
        }
    }

    // apply a new sync
    applySync() {
        if (!this.newSync) {
            return;
        }

        this.gameEngine.trace.debug('extrapolate applying sync');

        // create objects which are created at this step
        let world = this.gameEngine.world;
        for (let ids of Object.keys(this.newSync.syncObjects)) {
            this.newSync.syncObjects[ids].forEach(ev => {
                let curObj = world.objects[ev.objectInstance.id];

                if (curObj) {
                    this.gameEngine.trace.trace(`object before syncTo: ${curObj.toString()}`);
                    curObj.syncTo(ev.objectInstance, ev.stepCount, this.options.bending);
                    this.gameEngine.trace.trace(`object after syncTo: ${curObj.toString()} synced to step[${ev.stepCount}]`);
                } else {
                    this.addNewObject(ev.objectInstance.id, ev.objectInstance);
                }
            });
        }

        // re-apply the number of steps that we want to extrapolate forwards
        this.cleanRecentInputs();
        let step = world.stepCount - this.options.extrapolate;
        this.gameEngine.trace.debug(`extrapolate re-enacting steps from [${step}] to [${world.stepCount}]`);
        for (; step < world.stepCount; step++) {
            if (this.recentInputs[step]) {
                this.recentInputs[step].forEach(inputData => {
                    this.gameEngine.trace.trace(`extrapolate re-enacting input [${inputData.input}]`);
                    this.gameEngine.processInput(inputData, this.clientEngine.playerId);
                });
            }

            for (let objId of Object.keys(world.objects)) {
                this.gameEngine.trace.trace(`extrapolate re-enacting step on obj[${objId}]`);
                world.objects[objId].step(this.gameEngine.worldSettings);
            }
        }

        // trace object state after sync
        for (let objId of Object.keys(world.objects)) {
            this.gameEngine.trace.trace(`object after extrapolate replay: ${world.objects[objId].toString()}`);
        }

        // destroy uneeded objects
        // TODO: use this.forEachSyncObject instead of for-loop
        //       you will need to loop over prevObj instead of nextObj
        for (let objId of Object.keys(world.objects)) {
            if (objId < this.gameEngine.options.clientIDSpace && !this.newSync.syncObjects.hasOwnProperty(objId)) {
                world.objects[objId].destroy();
                delete this.gameEngine.world.objects[objId];
            }
        }

        this.newSync = null;
    }

    /**
     * Perform client-side extrapolation.
     */
    extrapolate() {

        // if there is a sync from the server, apply it now
        this.applySync();
    }
}

module.exports = ExtrapolateStrategy;
<commit_msg>reduce bending - re-apply since highest sync step<commit_after>"use strict";

const SyncStrategy = require("./SyncStrategy");

const defaults = {
    syncsBufferLength: 5,
    RTTEstimate: 2,       // estimate the RTT as two steps (for updateRate=6, that's 200ms)
    extrapolate: 2,       // player performs method "X" which means extrapolate to match server time. that 100 + (0..100)
    bending: 0.3          // amount of bending towards position of sync object
};

class ExtrapolateStrategy extends SyncStrategy {

    constructor(clientEngine, inputOptions) {

        const options = Object.assign({}, defaults, inputOptions);
        super(clientEngine, options);

        this.newSync = null;
        this.recentInputs = {};
        this.gameEngine = this.clientEngine.gameEngine;
        this.gameEngine.on('postStep', this.extrapolate.bind(this));
        this.gameEngine.on('client.syncReceived', this.collectSync.bind(this));
        this.gameEngine.on('client.preInput', this.clientInputSave.bind(this));
    }

    // keep a buffer of inputs so that we can replay them on extrapolation
    clientInputSave(inputData) {

        // if no inputs have been stored for this step, create an array
        if (!this.recentInputs[inputData.step]) {
            this.recentInputs[inputData.step] = [];
        }
        this.recentInputs[inputData.step].push(inputData);
    }

    // collect a sync and its events
    collectSync(e) {
        // TODO avoid editing the input event

        // keep a reference of events by object id
        e.syncObjects = {};
        e.syncEvents.forEach(sEvent => {
            let o = sEvent.objectInstance;
            if (!e.syncObjects[o.id]) {
                e.syncObjects[o.id] = [];
            }
            e.syncObjects[o.id].push(sEvent);
        });

        // keep a reference of events by step
        e.syncSteps = {};
        e.syncEvents.forEach(sEvent => {

            // add an entry for this step and event-name
            if (!e.syncSteps[sEvent.stepCount]) e.syncSteps[sEvent.stepCount] = {};
            if (!e.syncSteps[sEvent.stepCount][sEvent.eventName]) e.syncSteps[sEvent.stepCount][sEvent.eventName] = [];
            e.syncSteps[sEvent.stepCount][sEvent.eventName].push(sEvent);
        });

        // remember this sync
        this.newSync = e;
    }

    // add an object to our world
    addNewObject(objId, newObj) {

        let curObj = newObj.class.newFrom(newObj);
        this.gameEngine.addObjectToWorld(curObj);
        curObj.initRenderObject(this.gameEngine.renderer);
        console.log(`adding new object ${curObj}`);

        // if this game keeps a physics engine on the client side,
        // we need to update it as well
        if (this.gameEngine.physicsEngine) {
            curObj.initPhysicsObject(this.gameEngine.physicsEngine);
        }

        return curObj;
    }

    // clean up the input buffer
    cleanRecentInputs() {
        let firstReplayStep = this.gameEngine.world.stepCount - this.options.extrapolate;
        for (let input of Object.keys(this.recentInputs)) {
            if (this.recentInputs[input].step < firstReplayStep) {
                delete this.recentInputs[input];
            }
        }
    }

    // apply a new sync
    applySync() {
        if (!this.newSync) {
            return;
        }

        this.gameEngine.trace.debug('extrapolate applying sync');

        // create objects which are created at this step
        let world = this.gameEngine.world;
        let serverStep = -1;
        for (let ids of Object.keys(this.newSync.syncObjects)) {
            this.newSync.syncObjects[ids].forEach(ev => {
                let curObj = world.objects[ev.objectInstance.id];
                serverStep = Math.max(serverStep, ev.stepCount);

                if (curObj) {
                    this.gameEngine.trace.trace(`object before syncTo: ${curObj.toString()}`);
                    curObj.syncTo(ev.objectInstance, ev.stepCount, this.options.bending);
                    this.gameEngine.trace.trace(`object after syncTo: ${curObj.toString()} synced to step[${ev.stepCount}]`);
                } else {
                    this.addNewObject(ev.objectInstance.id, ev.objectInstance);
                }
            });
        }

        // re-apply the number of steps that we want to extrapolate forwards
        this.cleanRecentInputs();
        // let step = world.stepCount - this.options.extrapolate;
        let step = serverStep;
        this.gameEngine.trace.debug(`extrapolate re-enacting steps from [${step}] to [${world.stepCount}]`);
        for (; step < world.stepCount; step++) {
            if (this.recentInputs[step]) {
                this.recentInputs[step].forEach(inputData => {
                    this.gameEngine.trace.trace(`extrapolate re-enacting input [${inputData.input}]`);
                    this.gameEngine.processInput(inputData, this.clientEngine.playerId);
                });
            }

            for (let objId of Object.keys(world.objects)) {
                this.gameEngine.trace.trace(`extrapolate re-enacting step on obj[${objId}]`);
                world.objects[objId].step(this.gameEngine.worldSettings);
            }
        }

        // trace object state after sync
        for (let objId of Object.keys(world.objects)) {
            this.gameEngine.trace.trace(`object after extrapolate replay: ${world.objects[objId].toString()}`);
        }

        // destroy uneeded objects
        // TODO: use this.forEachSyncObject instead of for-loop
        //       you will need to loop over prevObj instead of nextObj
        for (let objId of Object.keys(world.objects)) {
            if (objId < this.gameEngine.options.clientIDSpace && !this.newSync.syncObjects.hasOwnProperty(objId)) {
                world.objects[objId].destroy();
                delete this.gameEngine.world.objects[objId];
            }
        }

        this.newSync = null;
    }

    /**
     * Perform client-side extrapolation.
     */
    extrapolate() {

        // if there is a sync from the server, apply it now
        this.applySync();
    }
}

module.exports = ExtrapolateStrategy;
<|endoftext|>


<commit_before>/**
 * Created by Jackey Li on 15/9/24.
 */
(function (angular) {
    'use strict';

    var moduleName = 'desktop';

    angular.module(moduleName)
        .controller('desktopController',
        ['$scope', '$translate',
            function ($scope, $translate) {
                $scope.desktop ={
                    dash:$translate.instant('desktop.dash'),
                    chats:$translate.instant('desktop.chats'),
                    account:$translate.instant('desktop.account')
                };
            }]);
})(angular);<commit_msg>add comment<commit_after>/**
 * Created by Jackey Li on 15/9/24.
 */
(function (angular) {
    'use strict';

    var moduleName = 'desktop';

    /**
     * desktop controller
     */
    angular.module(moduleName)
        .controller('desktopController',
        ['$scope', '$translate',
            function ($scope, $translate) {
                $scope.desktop ={
                    dash:$translate.instant('desktop.dash'),
                    chats:$translate.instant('desktop.chats'),
                    account:$translate.instant('desktop.account')
                };
            }]);
})(angular);<|endoftext|>


<commit_before>/* This file is part of Indico.
 * Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

(function(global) {
    'use strict';

    function getSelectedRows() {
        return $('.registrations input:checkbox:checked').map(function() {
            return $(this).val();
        }).get();
    }

    function handleRowSelection() {
        $('table.i-table input.select-row').on('change', function() {
            $(this).closest('tr').toggleClass('selected', this.checked);
            $('.js-requires-selected-row').toggleClass('disabled', !$('.registrations input:checkbox:checked').length);
            $('.regform-download-attachments').toggleClass('disabled', !$('.registrations input:checkbox:checked[data-has-files=true]').length);
        }).trigger('change');
    }

    function setupStaticURLGeneration() {
        $('.js-static-url').on('click', function() {
            var $this = $(this);
            $.ajax({
                method: 'POST',
                url: $this.data('href'),
                error: handleAjaxError,
                complete: IndicoUI.Dialogs.Util.progress(),
                success: function(data) {
                    $this.copyURLTooltip(data.url);
                }
            });
        });
    }

    function setupTableSorter() {
        $('.registrations .tablesorter').tablesorter({
            cssAsc: 'header-sort-asc',
            cssDesc: 'header-sort-desc',
            headerTemplate: '',
            headers: {
                0: {
                    sorter: false
                }
            }
        });
    }

    global.setupRegistrationList = function setupRegistrationList() {
        handleRowSelection();
        setupStaticURLGeneration();
        setupTableSorter();

        $('.registrations .toolbar')
        .dropdown()
        .on('click', '.js-dialog-action', function(e) {
            e.preventDefault();
            var $this = $(this);
            ajaxDialog({
                dialogClasses: 'reglist-filter-dialog',
                trigger: this,
                url: $this.data('href'),
                title: $this.data('title'),
                onClose: function(data) {
                    if (data) {
                        $('.registrations-table-wrapper').html(data.registration_list);
                        handleRowSelection();
                        setupTableSorter();
                        $('.js-customize-report').toggleClass('highlight', data.filtering_enabled);
                    }
                }
            });
        });

        $('#select-all').on('click', function() {
            $('table.i-table input.select-row').prop('checked', true).trigger('change');
        });

        $('#select-none').on('click', function() {
            $('table.i-table input.select-row').prop('checked', false).trigger('change');
        });

        $('.js-dialog-send-email').ajaxDialog({
            getExtraData: function() {
                return {registration_id: getSelectedRows()};
            }
        });

        $('.js-submit-reglist-form').on('click', function(e) {
            e.preventDefault();
            var $this = $(this);
            if (!$this.hasClass('disabled')) {
                $('.registrations form').attr('action', $this.data('href')).submit();
            }
        });

        $('.registrations').on('indico:confirmed', '.js-delete-registrations', function(evt) {
            evt.preventDefault();
            var $this = $(this);
            var selectedRows = getSelectedRows();
            $.ajax({
                url: $this.data('href'),
                method: $this.data('method'),
                data: {registration_id: selectedRows},
                traditional: true,
                complete: IndicoUI.Dialogs.Util.progress(),
                error: handleAjaxError,
                success: function() {
                    for (var i = 0; i < selectedRows.length; i++) {
                        var row = $('#registration-' + selectedRows[i]);
                        row.fadeOut('fast', function() {
                            $(this).remove();
                        });
                    }
                }
            });
        });

        $('.registrations').on('indico:confirmed', '.js-modify-status', function(evt) {
            evt.preventDefault();
            var $this = $(this);
            var selectedRows = getSelectedRows();
            $.ajax({
                url: $this.data('href'),
                method: $this.data('method'),
                data: {
                    registration_id: selectedRows,
                    approve: $this.data('approve')
                },
                traditional: true,
                complete: IndicoUI.Dialogs.Util.progress(),
                error: handleAjaxError,
                success: function(data) {
                    if (data) {
                        $('.registrations-table-wrapper').html(data.registration_list);
                        handleRowSelection();
                        setupTableSorter();
                    }
                }
            });
        });

        $('.registrations .toolbar').on('click', '.disabled', function(e) {
            e.preventDefault();
            e.stopPropagation();
        });

        var principal = $('#indico-user-to-add').principalfield({
            onUpdate: function(users) {
                if (users.length) {
                    var url = $('.js-add-user').data('href');
                    location.href = build_url(url, {user: users[0].id});
                }
            }
        });

        $('.js-add-user').on('click', function() {
            principal.principalfield('choose');
        });
    };

    function colorizeFilter(filter) {
        filter.toggleClass('active', filter.find(':checked').length > 0);
    }

    function colorizeActiveFilters() {
        $('.reglist-filter .filter').each(function() {
            colorizeFilter($(this));
        });
    }

    global.setupRegistrationListFilter = function setupRegistrationListFilter() {
        $('.reglist-filter').dropdown({selector: '.reglist-column .title'});
        colorizeActiveFilters();

        var visibleColumnsRegItemsField = $('#visible-columns-reg-items');
        var regItemsData = JSON.parse(visibleColumnsRegItemsField.val());

        $('.reglist-column')
        .on('click', '.trigger', function() {
            var $this = $(this);
            var field = $this.closest('.reglist-column');
            var fieldId = field.data('id');
            var enabled = $this.hasClass('enabled');

            if (enabled) {
                regItemsData.splice(regItemsData.indexOf(fieldId), 1);
            } else {
                regItemsData.push(fieldId);
            }

            $this.toggleClass('enabled', !enabled);
            field.toggleClass('striped', enabled);
            visibleColumnsRegItemsField.val(JSON.stringify(regItemsData)).trigger('change');
        })
        .each(function() {
            var field = $(this);
            var fieldId = field.data('id');

            if (regItemsData.indexOf(fieldId) != -1) {
                field.find('.trigger').addClass('enabled');
            } else {
                field.addClass('striped');
            }
        });

        $('.js-reset-btn').on('click', function() {
            $('.reglist-filter input:checkbox').prop('checked', false).trigger('change');
            $('.js-clear-filters-message').show({
                done: function() {
                    var $this = $(this);
                    setTimeout(function() {
                        $this.slideUp();
                    }, 4000);
                }
            });
        });

        $('.reglist-filter input:checkbox').on('change', function() {
            colorizeFilter($(this).closest('.filter'));
        });
    };
})(window);
<commit_msg>Clear filters: only select checked checkboxes<commit_after>/* This file is part of Indico.
 * Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

(function(global) {
    'use strict';

    function getSelectedRows() {
        return $('.registrations input:checkbox:checked').map(function() {
            return $(this).val();
        }).get();
    }

    function handleRowSelection() {
        $('table.i-table input.select-row').on('change', function() {
            $(this).closest('tr').toggleClass('selected', this.checked);
            $('.js-requires-selected-row').toggleClass('disabled', !$('.registrations input:checkbox:checked').length);
            $('.regform-download-attachments').toggleClass('disabled', !$('.registrations input:checkbox:checked[data-has-files=true]').length);
        }).trigger('change');
    }

    function setupStaticURLGeneration() {
        $('.js-static-url').on('click', function() {
            var $this = $(this);
            $.ajax({
                method: 'POST',
                url: $this.data('href'),
                error: handleAjaxError,
                complete: IndicoUI.Dialogs.Util.progress(),
                success: function(data) {
                    $this.copyURLTooltip(data.url);
                }
            });
        });
    }

    function setupTableSorter() {
        $('.registrations .tablesorter').tablesorter({
            cssAsc: 'header-sort-asc',
            cssDesc: 'header-sort-desc',
            headerTemplate: '',
            headers: {
                0: {
                    sorter: false
                }
            }
        });
    }

    global.setupRegistrationList = function setupRegistrationList() {
        handleRowSelection();
        setupStaticURLGeneration();
        setupTableSorter();

        $('.registrations .toolbar')
        .dropdown()
        .on('click', '.js-dialog-action', function(e) {
            e.preventDefault();
            var $this = $(this);
            ajaxDialog({
                dialogClasses: 'reglist-filter-dialog',
                trigger: this,
                url: $this.data('href'),
                title: $this.data('title'),
                onClose: function(data) {
                    if (data) {
                        $('.registrations-table-wrapper').html(data.registration_list);
                        handleRowSelection();
                        setupTableSorter();
                        $('.js-customize-report').toggleClass('highlight', data.filtering_enabled);
                    }
                }
            });
        });

        $('#select-all').on('click', function() {
            $('table.i-table input.select-row').prop('checked', true).trigger('change');
        });

        $('#select-none').on('click', function() {
            $('table.i-table input.select-row').prop('checked', false).trigger('change');
        });

        $('.js-dialog-send-email').ajaxDialog({
            getExtraData: function() {
                return {registration_id: getSelectedRows()};
            }
        });

        $('.js-submit-reglist-form').on('click', function(e) {
            e.preventDefault();
            var $this = $(this);
            if (!$this.hasClass('disabled')) {
                $('.registrations form').attr('action', $this.data('href')).submit();
            }
        });

        $('.registrations').on('indico:confirmed', '.js-delete-registrations', function(evt) {
            evt.preventDefault();
            var $this = $(this);
            var selectedRows = getSelectedRows();
            $.ajax({
                url: $this.data('href'),
                method: $this.data('method'),
                data: {registration_id: selectedRows},
                traditional: true,
                complete: IndicoUI.Dialogs.Util.progress(),
                error: handleAjaxError,
                success: function() {
                    for (var i = 0; i < selectedRows.length; i++) {
                        var row = $('#registration-' + selectedRows[i]);
                        row.fadeOut('fast', function() {
                            $(this).remove();
                        });
                    }
                }
            });
        });

        $('.registrations').on('indico:confirmed', '.js-modify-status', function(evt) {
            evt.preventDefault();
            var $this = $(this);
            var selectedRows = getSelectedRows();
            $.ajax({
                url: $this.data('href'),
                method: $this.data('method'),
                data: {
                    registration_id: selectedRows,
                    approve: $this.data('approve')
                },
                traditional: true,
                complete: IndicoUI.Dialogs.Util.progress(),
                error: handleAjaxError,
                success: function(data) {
                    if (data) {
                        $('.registrations-table-wrapper').html(data.registration_list);
                        handleRowSelection();
                        setupTableSorter();
                    }
                }
            });
        });

        $('.registrations .toolbar').on('click', '.disabled', function(e) {
            e.preventDefault();
            e.stopPropagation();
        });

        var principal = $('#indico-user-to-add').principalfield({
            onUpdate: function(users) {
                if (users.length) {
                    var url = $('.js-add-user').data('href');
                    location.href = build_url(url, {user: users[0].id});
                }
            }
        });

        $('.js-add-user').on('click', function() {
            principal.principalfield('choose');
        });
    };

    function colorizeFilter(filter) {
        filter.toggleClass('active', filter.find(':checked').length > 0);
    }

    function colorizeActiveFilters() {
        $('.reglist-filter .filter').each(function() {
            colorizeFilter($(this));
        });
    }

    global.setupRegistrationListFilter = function setupRegistrationListFilter() {
        $('.reglist-filter').dropdown({selector: '.reglist-column .title'});
        colorizeActiveFilters();

        var visibleColumnsRegItemsField = $('#visible-columns-reg-items');
        var regItemsData = JSON.parse(visibleColumnsRegItemsField.val());

        $('.reglist-column')
        .on('click', '.trigger', function() {
            var $this = $(this);
            var field = $this.closest('.reglist-column');
            var fieldId = field.data('id');
            var enabled = $this.hasClass('enabled');

            if (enabled) {
                regItemsData.splice(regItemsData.indexOf(fieldId), 1);
            } else {
                regItemsData.push(fieldId);
            }

            $this.toggleClass('enabled', !enabled);
            field.toggleClass('striped', enabled);
            visibleColumnsRegItemsField.val(JSON.stringify(regItemsData)).trigger('change');
        })
        .each(function() {
            var field = $(this);
            var fieldId = field.data('id');

            if (regItemsData.indexOf(fieldId) != -1) {
                field.find('.trigger').addClass('enabled');
            } else {
                field.addClass('striped');
            }
        });

        $('.js-reset-btn').on('click', function() {
            $('.reglist-filter input:checkbox:checked').prop('checked', false).trigger('change');
            $('.js-clear-filters-message').show({
                done: function() {
                    var $this = $(this);
                    setTimeout(function() {
                        $this.slideUp();
                    }, 4000);
                }
            });
        });

        $('.reglist-filter input:checkbox').on('change', function() {
            colorizeFilter($(this).closest('.filter'));
        });
    };
})(window);
<|endoftext|>