<commit_before>/* Friends List system
 * This is a simple friends list system for Pokemon Showdown.
 * It will alert you when your friends come online.  It also
 * will show you the last time they were online on the server.
 * by: panpawn
 */

var fs = require('fs');
var moment = require('moment');
var Friends = {};
var filepath = 'config/friends.json';

function loadFriendsList() {
	try {
		Friends = JSON.parse(fs.readFileSync(filepath));
	} catch (e) {
		Friends = {};
	}
}
loadFriendsList();

function updateFriends() {
	fs.writeFile(filepath, JSON.stringify(Friends));
}
function formatList(user, by) {
	if (!Friends[user]) Friends[user] = [];
	var reply = "
Friends:
";
		reply += '
';
		reply += "
Friend:	Last Online:	Bucks:
";
	Friends[user].forEach(function(frens) {
		function lastSeen(frens) {
			if (Users(frens) && Users.getExact(frens) && Users(frens).connected) return "Currently Online";
			if (!Gold.seenData[frens]) return "Never seen on this server";
			var userLastSeen = moment(Gold.seenData[frens]).format("MMMM Do YYYY, h:mm:ss a");
			return userLastSeen;
		}
		reply += "
" + (Users.getExact(frens) && Users(frens).connected ? Users.getExact(frens).name : frens) + "	" + lastSeen(frens) + "	" + (economy.readMoney(frens) == 0 ? "None" : economy.readMoney(frens)) + "
";
	});
	reply += "

";
	return reply;
}

exports.commands = {
	frens: 'friendslist',
	friends: 'friendslist',
	friend: 'friendslist',
	friendlist: 'friendslist',
	friendslist: function (target, room, user) {
		target = target.split(', ');
		if (!Friends[user.userid]) {
			Friends[user.userid] = [];
			this.parse('/help friendslist');
		}
		switch (target[0]) {
			case 'add':
				var newFriend = toId(target[1]);
				if (user.userid === newFriend) return this.errorReply("You cannot add yourself to your friendslist...");
				if (newFriend.length > 18) return this.errorReply("Usernames are not this long...");
				if (~Friends[user.userid].indexOf(newFriend)) return this.errorReply("You are already friends with this person!");
				Friends[user.userid].push(newFriend);
				updateFriends();
				return this.sendReply("|raw|You have added " + Tools.escapeHTML(target[1]) + " to your friends list.");
				break;
			case 'delete':
			case 'remove':
				var removee = toId(target[1]);
				if (!~Friends[user.userid].indexOf(removee)) return this.errorReply("You are not currently friends with this user.  Check spelling?");
				Friends[user.userid].remove(removee);
				updateFriends();
				return this.sendReply("|raw|You have unfriended " + Tools.escapeHTML(removee) + " from your friends list.");
				break;
			default:
				if (!this.canBroadcast()) return;
				if (!target[0]) {
					if (!Friends[user.userid] || Friends[user.userid].length < 1) return this.errorReply("You do not have any friends added to your friendslist yet.");
					return this.sendReplyBox(formatList(user.userid, user.userid));
				} else {
					target[0] = toId(target[0]);
					if (!Friends[target[0]] || Friends[target[0]].length < 1) return this.errorReply("This user does not have any friends added to their friendslist yet.");
					return this.sendReplyBox(formatList(target[0], user.userid));
				}
				break;
		}
	},
	friendslisthelp: ["Gold's friendslist allows users to add friends to their friendslists. The commands include...",
					"/friendslist add, [user] - Adds a user to your friendslist.",
					"/friendslist remove, [user] - Removes a user from your friendslist.",
					"/friendslist - Displays your friendslist.",
					"/friendslist [user] - Displays [user]'s friendslist."],
};

Gold.friends = Friends;
<commit_msg>Even more updates to friendslists<commit_after>/* Friends List system
 * This is a simple friends list system for Pokemon Showdown.
 * It will alert you when your friends come online.  It also
 * will show you the last time they were online on the server.
 * by: panpawn
 */

var fs = require('fs');
var moment = require('moment');
var Friends = {};
var filepath = 'config/friends.json';

function loadFriendsList() {
	try {
		Friends = JSON.parse(fs.readFileSync(filepath));
	} catch (e) {
		Friends = {};
	}
}
loadFriendsList();

function updateFriends() {
	fs.writeFile(filepath, JSON.stringify(Friends));
}
function formatList(user, by) {
	if (!Friends[user]) Friends[user] = [];
	var reply = "
Friends:
";
		reply += '
';
		reply += "
Friend:	Last Online:	Bucks:
";
	Friends[user].forEach(function(frens) {
		function lastSeen(frens) {
			if (Users(frens) && Users.getExact(frens) && Users(frens).connected) return "Currently Online";
			if (!Gold.seenData[frens]) return "Never seen on this server";
			var userLastSeen = moment(Gold.seenData[frens]).format("MMMM Do YYYY, h:mm:ss a");
			return userLastSeen;
		}
		reply += "
" + (Users.getExact(frens) && Users(frens).connected ? Users.getExact(frens).name : frens) + "	" + lastSeen(frens) + "	" + (economy.readMoney(frens) == 0 ? "None" : economy.readMoney(frens)) + "
";
	});
	reply += "

";
	return reply;
}

exports.commands = {
	frens: 'friendslist',
	friends: 'friendslist',
	friend: 'friendslist',
	friendlist: 'friendslist',
	friendslist: function (target, room, user) {
		target = target.split(', ');
		if (!Friends[user.userid]) {
			Friends[user.userid] = [];
			this.parse('/help friendslist');
		}
		switch (target[0]) {
			case 'add':
				var newFriend = toId(target[1]);
				if (!newFriend) return this.errorReply("Usage: /friendslist add, [user] - Adds a user to your friendslist.");
				if (user.userid === newFriend) return this.errorReply("You cannot add yourself to your friendslist...");
				if (newFriend.length > 18) return this.errorReply("Usernames are not this long...");
				if (~Friends[user.userid].indexOf(newFriend)) return this.errorReply("You are already friends with this person!");
				Friends[user.userid].push(newFriend);
				updateFriends();
				return this.sendReply("|raw|You have added " + Tools.escapeHTML(target[1]) + " to your friends list.");
				break;
			case 'delete':
			case 'remove':
				var removee = toId(target[1]);
				if (!removee) return this.errorReply("Usage: /friendslist remove, [user] - Removes a user from your friendslist.");
				if (!~Friends[user.userid].indexOf(removee)) return this.errorReply("You are not currently friends with this user.  Check spelling?");
				Friends[user.userid].remove(removee);
				updateFriends();
				return this.sendReply("|raw|You have unfriended " + Tools.escapeHTML(removee) + " from your friends list.");
				break;
			case 'deleteall':
			case 'removeall':
				if (!Friends[user.userid] || Friends[user.userid].lenth < 1) return this.errorReply("You do not have any friends added to your friendslist yet.");
				Friends[user.userid] = [];
				updateFriends();
				return this.sendReply("You have cleared your friendslist.");
				break;
			default:
				if (!this.canBroadcast()) return;
				if (!target[0]) {
					if (!Friends[user.userid] || Friends[user.userid].length < 1) return this.errorReply("You do not have any friends added to your friendslist yet.");
					return this.sendReplyBox(formatList(user.userid, user.userid));
				} else {
					target[0] = toId(target[0]);
					if (!Friends[target[0]] || Friends[target[0]].length < 1) return this.errorReply("This user does not have any friends added to their friendslist yet.");
					return this.sendReplyBox(formatList(target[0], user.userid));
				}
				break;
		}
	},
	friendslisthelp: ["Gold's friendslist allows users to add friends to their friendslists. The commands include...",
					"/friendslist add, [user] - Adds a user to your friendslist.",
					"/friendslist remove, [user] - Removes a user from your friendslist.",
					"/friendslist removeall - Clears your friendslist.",
					"/friendslist - Displays your friendslist.",
					"/friendslist [user] - Displays [user]'s friendslist."],
};

Gold.friends = Friends;
<|endoftext|>


<commit_before>import 'babel-polyfill';
import './fib-gen';

<commit_msg>Refactor example codes<commit_after>// @flow

import 'source-map-support/register';
import 'babel-polyfill';
import 'colors';
// import async from 'async';
// import _ from 'lodash';
// import console from 'better-console';

// import './fib-gen';
import './table';
import './random-throw';
<|endoftext|>


<commit_before>import path from 'path'
import Promise from 'bluebird'
import gulp from 'gulp'
import webpackStream from 'webpack-stream'
import del from 'del'
import gulpUglify from 'gulp-uglify'
import gulpStreamify from 'gulp-streamify'
import gulpDebug from 'gulp-debug'
import gulpPlumber from 'gulp-plumber'
import gulpStylus from 'gulp-stylus'
import gulpRename from 'gulp-rename'
import gutil from 'gulp-util'
import { merge, clone, typeOf } from 'lutils'

export default class Tasks {
    /**
     *    Tasks
     *
     *    @param     {Object}    config = {}
     */
    constructor(config = {}) {
        this.config = merge(
            clone( require('./config') ),
            config
        )

        console.log(this.config)
    }

    /**
     *    Builds scripts and styles
     *
     *    @param     {Object}    overrides    Options to overwrite for each entry
     *
     *    @return    {Promise}
     */
    build(overrides) {
        return Promise.all([
            ...this.scripts(overrides),
            ...this.styles(overrides),
        ])
    }

    /**
     *    Purges dist folder
     *
     */
    clean() {
        return del( path.join(this.config.dist, './*') )
    }

    /**
     *    Builds configured styles entries and optionally watches for changes
     *
     *    @param     {Object}    overrides    =    { watch: true }
     *
     *    @return    {Array of Promise}
     */
    styles(overrides = {}) {
        if ( ! typeOf.Array(this.config.styles) ) return []

        return this.config.styles.map((options) =>
            this.stylus({
                ...merge(options, overrides),
                source    : path.resolve(this.config.source, options.source),
                dist      : path.resolve(this.config.dist, options.dist),
                watchGlob : path.resolve(this.config.source, options.watchGlob || ''),
            })
        )
    }

    /**
     *    Builds configured scripts entries and optionally watches for changes
     *
     *    @param     {Object}    overrides    =    { watch: true }
     *
     *    @return    {Array of Promise}
     */
    scripts(overrides = {}) {
        if ( ! typeOf.Array(this.config.scripts) ) return []

        return this.config.scripts.map((options) =>
            this.webpack({
                ...merge(options, overrides),
                source   : path.resolve(this.config.source, options.source),
                dist     : path.resolve(this.config.dist, options.dist),
                watch    : options.watch,
            })
        )
    }

    /**
     *    Renders stylus to css to dist
     *
     *    @return    {Promise}
     */
    stylus(options) {
        const run = () =>
            gulp.src(options.source)
                .pipe(gulpPlumber())
                .pipe(gulpDebug({title: 'stylus'}))
                .pipe(gulpStylus({
                    'include css' : true,
                    'compress'    : Boolean(options.compress),
                }))
                .pipe(gulpRename({ extname: options.extension || '.css' }))
                .pipe(gulp.dest(options.dist))

        if ( options.watch )
            return Promise.all([ run(), gulp.watch(options.watchGlob, run)])
        else
            return run()
    }

    /**
     *    Bundles js to dist
     *
     *    @return    {Promise}
     */
    webpack(options) {
        options.babel = options.babel || {}

        options.babel.presets = [
            ...this.config.webpack.presets,
            ...options.babel.presets
        ]

        options.babel.plugins = [
            ...this.config.webpack.plugins,
            ...options.babel.plugins
        ]

        const loaders = [
            {
                test    : /\.jsx?$/i,
                exclude : /node_modules/,
                loader  : 'babel-loader',
                query   : options.babel || {}
            },
            ...this.config.webpack.loaders
        ]

        let config = {
            watch   : options.watch,
            entry   : [ 'babel-polyfill', options.source ],
            devtool : 'source-map',
            module  : { loaders },
            output: {
                filename          : path.basename(options.source),
                sourceMapFilename : `${path.basename(options.source)}.map`,
            },

            eslint: this.config.webpack.eslint,
        }

        if ( options.webpack ) config = merge( clone(config), options.webpack )

        let g = gulp.src(options.source)
            .pipe( gulpPlumber() )
            .pipe( gulpDebug({title: 'Webpack'}) )
            .pipe( webpackStream(config) )
            //.on('error', gutil.log.bind(gutil, 'Webpack Error'))
            // TODO: handle error better. check out .old/cfg for the webpack-errors module used etc.

        // TODO: configure uglify
        if ( options.compress ) g = g.pipe( gulpStreamify( gulpUglify() ) )

        return g.pipe(gulp.dest(options.dist))

    }
}
<commit_msg>remove console.log<commit_after>import path from 'path'
import Promise from 'bluebird'
import gulp from 'gulp'
import webpackStream from 'webpack-stream'
import del from 'del'
import gulpUglify from 'gulp-uglify'
import gulpStreamify from 'gulp-streamify'
import gulpDebug from 'gulp-debug'
import gulpPlumber from 'gulp-plumber'
import gulpStylus from 'gulp-stylus'
import gulpRename from 'gulp-rename'
import gutil from 'gulp-util'
import { merge, clone, typeOf } from 'lutils'

export default class Tasks {
    /**
     *    Tasks
     *
     *    @param     {Object}    config = {}
     */
    constructor(config = {}) {
        this.config = merge(
            clone( require('./config') ),
            config
        )
    }

    /**
     *    Builds scripts and styles
     *
     *    @param     {Object}    overrides    Options to overwrite for each entry
     *
     *    @return    {Promise}
     */
    build(overrides) {
        return Promise.all([
            ...this.scripts(overrides),
            ...this.styles(overrides),
        ])
    }

    /**
     *    Purges dist folder
     *
     */
    clean() {
        return del( path.join(this.config.dist, './*') )
    }

    /**
     *    Builds configured styles entries and optionally watches for changes
     *
     *    @param     {Object}    overrides    =    { watch: true }
     *
     *    @return    {Array of Promise}
     */
    styles(overrides = {}) {
        if ( ! typeOf.Array(this.config.styles) ) return []

        return this.config.styles.map((options) =>
            this.stylus({
                ...merge(options, overrides),
                source    : path.resolve(this.config.source, options.source),
                dist      : path.resolve(this.config.dist, options.dist),
                watchGlob : path.resolve(this.config.source, options.watchGlob || ''),
            })
        )
    }

    /**
     *    Builds configured scripts entries and optionally watches for changes
     *
     *    @param     {Object}    overrides    =    { watch: true }
     *
     *    @return    {Array of Promise}
     */
    scripts(overrides = {}) {
        if ( ! typeOf.Array(this.config.scripts) ) return []

        return this.config.scripts.map((options) =>
            this.webpack({
                ...merge(options, overrides),
                source   : path.resolve(this.config.source, options.source),
                dist     : path.resolve(this.config.dist, options.dist),
                watch    : options.watch,
            })
        )
    }

    /**
     *    Renders stylus to css to dist
     *
     *    @return    {Promise}
     */
    stylus(options) {
        const run = () =>
            gulp.src(options.source)
                .pipe(gulpPlumber())
                .pipe(gulpDebug({title: 'stylus'}))
                .pipe(gulpStylus({
                    'include css' : true,
                    'compress'    : Boolean(options.compress),
                }))
                .pipe(gulpRename({ extname: options.extension || '.css' }))
                .pipe(gulp.dest(options.dist))

        if ( options.watch )
            return Promise.all([ run(), gulp.watch(options.watchGlob, run)])
        else
            return run()
    }

    /**
     *    Bundles js to dist
     *
     *    @return    {Promise}
     */
    webpack(options) {
        options.babel = options.babel || {}

        options.babel.presets = [
            ...this.config.webpack.presets,
            ...options.babel.presets
        ]

        options.babel.plugins = [
            ...this.config.webpack.plugins,
            ...options.babel.plugins
        ]

        const loaders = [
            {
                test    : /\.jsx?$/i,
                exclude : /node_modules/,
                loader  : 'babel-loader',
                query   : options.babel || {}
            },
            ...this.config.webpack.loaders
        ]

        let config = {
            watch   : options.watch,
            entry   : [ 'babel-polyfill', options.source ],
            devtool : 'source-map',
            module  : { loaders },
            output: {
                filename          : path.basename(options.source),
                sourceMapFilename : `${path.basename(options.source)}.map`,
            },

            eslint: this.config.webpack.eslint,
        }

        if ( options.webpack ) config = merge( clone(config), options.webpack )

        let g = gulp.src(options.source)
            .pipe( gulpPlumber() )
            .pipe( gulpDebug({title: 'Webpack'}) )
            .pipe( webpackStream(config) )
            //.on('error', gutil.log.bind(gutil, 'Webpack Error'))
            // TODO: handle error better. check out .old/cfg for the webpack-errors module used etc.

        // TODO: configure uglify
        if ( options.compress ) g = g.pipe( gulpStreamify( gulpUglify() ) )

        return g.pipe(gulp.dest(options.dist))

    }
}
<|endoftext|>


<commit_before>const models = require('../../models');
let Project = models.projects;
let User = models.users;
let ProjectAssignment = models.project_assignments;

module.exports = function(connection, done) {
  connection.createChannel(function(err, ch) {
    console.log(err);
    var ex = 'chiepherd.main';
    ch.assertExchange(ex, 'topic');
    ch.assertQueue('chiepherd.project_assignment.create', { exclusive: false }, function(err, q) {
      ch.bindQueue(q.queue, ex, "chiepherd.project_assignment.create")

      ch.consume(q.queue, function(msg) {
        // LOG
        console.log(" [%s]: %s", msg.fields.routingKey, msg.content.toString());
        let json = JSON.parse(msg.content.toString());

        Project.find({
          where: {
            uuid: json.projectUuid
          }
        }).then(function (project) {
          if (project != null) {
            User.find({
              where: {
                email: json.email
              }
            }).then(function (user) {
              if (user != null) {
                ProjectAssignment.find({
                  where: {
                    uuid: json.projectAssignmentUuid,
                    userId: user.id
                  }
                }).then(function (assignment) {
                  if (assignment != null) {
                    projectAssignment.find({
                      where: {
                        projectId: project.id,
                        userId: user.id,
                        uuid: {
                          $not: assignment.uuid
                        }
                      }
                    }).then(function (assign) {
                      if (assign == null) {
                        assignment.update({
                          rank: json.rank || assignment.rank,
                          projectId: project.id
                        }).then(function (projectAssignment) {
                          projectAssignment.email = user.email;
                          projectAssignment.projectUuid = project.uuid;
                          ch.sendToQueue(msg.properties.replyTo,
                            new Buffer.from(JSON.stringify(projectAssignment.responsify())),
                            { correlationId: msg.properties.correlationId });
                          connection.createChannel(function(error, channel) {
                            var ex = 'chiepherd.project_assignment.created';
                            channel.assertExchange(ex, 'fanout', { durable: false });
                            channel.publish(ex, '', new Buffer.from(JSON.stringify(projectAssignment.responsify())));
                          });
                        }).catch(function (error) {
                          console.log(error);
                          ch.sendToQueue(msg.properties.replyTo,
                            new Buffer(error.toString()),
                            { correlationId: msg.properties.correlationId });
                          ch.ack(msg);
                        });
                      } else {
                        ch.sendToQueue(msg.properties.replyTo,
                          new Buffer("This user is already a member of this project."),
                          { correlationId: msg.properties.correlationId });
                        ch.ack(msg);
                      }
                    }).catch(function (error) {
                      console.log(error);
                      ch.sendToQueue(msg.properties.replyTo,
                        new Buffer(error.toString()),
                        { correlationId: msg.properties.correlationId });
                      ch.ack(msg);
                    });
                  } else {
                    ch.sendToQueue(msg.properties.replyTo,
                      new Buffer("This user already belongs to this project."),
                      { correlationId: msg.properties.correlationId });
                    ch.ack(msg);
                  }
                }).catch(function (error) {
                  console.log(error);
                  ch.sendToQueue(msg.properties.replyTo,
                    new Buffer(error.toString()),
                    { correlationId: msg.properties.correlationId });
                  ch.ack(msg);
                });
              } else {
                ch.sendToQueue(msg.properties.replyTo,
                  new Buffer("Unknown user."),
                  { correlationId: msg.properties.correlationId });
                ch.ack(msg);
              }
            }).catch(function (error) {
              console.log(error);
              ch.sendToQueue(msg.properties.replyTo,
                new Buffer(error.toString()),
                { correlationId: msg.properties.correlationId });
              ch.ack(msg);
            });
          } else {
            ch.sendToQueue(msg.properties.replyTo,
              new Buffer("Unknown project."),
              { correlationId: msg.properties.correlationId });
            ch.ack(msg);
          }
        }).catch(function (error) {
          console.log(error);
          ch.sendToQueue(msg.properties.replyTo,
            new Buffer(error.toString()),
            { correlationId: msg.properties.correlationId });
          ch.ack(msg);
        });
      }, { noAck: false });
    });
  });
  done();
}
<commit_msg>Fix routing key name on project_assignment update<commit_after>const models = require('../../models');
let Project = models.projects;
let User = models.users;
let ProjectAssignment = models.project_assignments;

module.exports = function(connection, done) {
  connection.createChannel(function(err, ch) {
    console.log(err);
    var ex = 'chiepherd.main';
    ch.assertExchange(ex, 'topic');
    ch.assertQueue('chiepherd.project_assignment.update', { exclusive: false }, function(err, q) {
      ch.bindQueue(q.queue, ex, "chiepherd.project_assignment.update")

      ch.consume(q.queue, function(msg) {
        // LOG
        console.log(" [%s]: %s", msg.fields.routingKey, msg.content.toString());
        let json = JSON.parse(msg.content.toString());

        Project.find({
          where: {
            uuid: json.projectUuid
          }
        }).then(function (project) {
          if (project != null) {
            User.find({
              where: {
                email: json.email
              }
            }).then(function (user) {
              if (user != null) {
                ProjectAssignment.find({
                  where: {
                    uuid: json.projectAssignmentUuid,
                    userId: user.id
                  }
                }).then(function (assignment) {
                  if (assignment != null) {
                    ProjectAssignment.find({
                      where: {
                        projectId: project.id,
                        userId: user.id,
                        uuid: {
                          $not: assignment.uuid
                        }
                      }
                    }).then(function (assign) {
                      if (assign == null) {
                        assignment.update({
                          rank: json.rank || assignment.rank,
                          projectId: project.id
                        }).then(function (projectAssignment) {
                          projectAssignment.email = user.email;
                          projectAssignment.projectUuid = project.uuid;
                          ch.sendToQueue(msg.properties.replyTo,
                            new Buffer.from(JSON.stringify(projectAssignment.responsify())),
                            { correlationId: msg.properties.correlationId });
                          connection.createChannel(function(error, channel) {
                            var ex = 'chiepherd.project_assignment.created';
                            channel.assertExchange(ex, 'fanout', { durable: false });
                            channel.publish(ex, '', new Buffer.from(JSON.stringify(projectAssignment.responsify())));
                          });
                        }).catch(function (error) {
                          console.log(error);
                          ch.sendToQueue(msg.properties.replyTo,
                            new Buffer(error.toString()),
                            { correlationId: msg.properties.correlationId });
                          ch.ack(msg);
                        });
                      } else {
                        ch.sendToQueue(msg.properties.replyTo,
                          new Buffer("This user is already a member of this project."),
                          { correlationId: msg.properties.correlationId });
                        ch.ack(msg);
                      }
                    }).catch(function (error) {
                      console.log(error);
                      ch.sendToQueue(msg.properties.replyTo,
                        new Buffer(error.toString()),
                        { correlationId: msg.properties.correlationId });
                      ch.ack(msg);
                    });
                  } else {
                    ch.sendToQueue(msg.properties.replyTo,
                      new Buffer("This user already belongs to this project."),
                      { correlationId: msg.properties.correlationId });
                    ch.ack(msg);
                  }
                }).catch(function (error) {
                  console.log(error);
                  ch.sendToQueue(msg.properties.replyTo,
                    new Buffer(error.toString()),
                    { correlationId: msg.properties.correlationId });
                  ch.ack(msg);
                });
              } else {
                ch.sendToQueue(msg.properties.replyTo,
                  new Buffer("Unknown user."),
                  { correlationId: msg.properties.correlationId });
                ch.ack(msg);
              }
            }).catch(function (error) {
              console.log(error);
              ch.sendToQueue(msg.properties.replyTo,
                new Buffer(error.toString()),
                { correlationId: msg.properties.correlationId });
              ch.ack(msg);
            });
          } else {
            ch.sendToQueue(msg.properties.replyTo,
              new Buffer("Unknown project."),
              { correlationId: msg.properties.correlationId });
            ch.ack(msg);
          }
        }).catch(function (error) {
          console.log(error);
          ch.sendToQueue(msg.properties.replyTo,
            new Buffer(error.toString()),
            { correlationId: msg.properties.correlationId });
          ch.ack(msg);
        });
      }, { noAck: false });
    });
  });
  done();
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014 - 2015 Björn Ricks <bjoern.ricks@gmail.com>
 *
 */
(function() {
    'use strict';

    angular.module('angTutApp',
        ['ui.router', 'ngMaterial', 'angTutApp.controllers']
    )
    .config(app_config)
    .factory('conf', conf)
    .factory('examplesService', examplesService)
    .factory('menu', menu)
    .directive('hl', hl)
    .directive('atHljs', at_hljs);

    app_config.$inject = ['$stateProvider', '$urlRouterProvider'];

    function app_config($stateProvider, $urlRouterProvider) {
        $stateProvider.
            state('base', {
                    templateUrl: 'base.html',
                    abstract: true,
                    controller: 'BaseCtrl',
                    controllerAs: 'base',
            }).
            state('home', {
                    url: '/',
                    parent: 'base',
                    templateUrl: 'home.html',
                    controller: 'HomeCtrl',
            }).
            state('example', {
                    parent: 'base',
                    url: '/examples/:exampleNumber',
                    templateUrl: 'example-view.html',
                    controller: 'ExampleDetailCtrl',
                    controllerAs: 'ctrl',
            }).
            state('section', {
                    parent: 'base',
                    url: '/sections/:sectionNumber',
                    templateUrl: 'section-view.html',
                    controller: 'SectionCtrl',
                    controllerAs: 'ctrl',
            });
        $urlRouterProvider.otherwise('/');
    }

    function conf() {
        var service = {
            examplesPath: 'examples/',
        };
        return service;
    }

    examplesService.$inject = ['$http', '$q', '$state'];

    function examplesService($http, $q, $state) {

        var section = null;
        var example = null;
        var examples = null;

        var service = {
            get: get_examples,
            setSection: set_section,
            setExample: set_example,
            getNext: get_next,
            hasNext: has_next,
            hasPrevious: has_previous,
            getPrevious: get_previous,
        };

        return service;

        function set_section(sectionid) {
            return get_examples().then(function(data) {
                for(var i=0; i < data.length; i++) {
                    var sec = data[i];
                    if (sec.id == sectionid) {
                        section = sec;
                        example = null;
                        return section;
                    }
                }
            });
        }

        function set_example(exampleid) {
            return get_examples().then(function(data) {
                for(var i=0; i < data.length; i++) {
                    var sec = data[i];
                    for(var j=0; j < sec.examples.length; j++) {
                        var ex = sec.examples[j];
                        if (ex.id == exampleid) {
                            section = sec;
                            example = ex;
                            return example;
                        }
                    }
                }
            });
        }

        function get_examples() {
            var deferred = $q.defer();

            if (examples !== null) {
                deferred.resolve(examples);
            }
            else {
                $http.get('examples.json', {cache: true}).then(
                    function(response) {
                        set_examples(response.data);
                        deferred.resolve(examples);
                    },
                    function(response) {
                        deferred.reject(response);
                    }
                );
            }

            return deferred.promise;
        }

        function get_next() {
            if (angular.isObject(example)) {
                if (angular.isObject(example.next)) {
                    return $state.href('example', {exampleNumber: example.next.id});
                }
                if (angular.isObject(section) && angular.isObject(section.next)) {
                    return $state.href('section', {sectionNumber: section.next.id});
                }
            }

            if (angular.isObject(section)) {
                if (section.examples.length > 0) {
                    return $state.href('example', {exampleNumber: section.examples[0].id});
                }
                if (angular.isObject(section.next)) {
                    return $state.href('section', {sectionNumber: section.next.id});
                }
            }

            return "#/";
        }

        function get_previous() {
            if (angular.isObject(example)) {
                if (angular.isObject(example.prev)) {
                    return $state.href('example', {exampleNumber: example.prev.id});
                }
                if (angular.isObject(section) && angular.isObject(section.prev)) {
                    return $state.href('section', {sectionNumber: section.prev.id});
                }
            }

            if (angular.isObject(section) && angular.isObject(section.prev)) {
                var nr = section.prev.examples.length - 1;
                if (nr > 0) {
                    return $state.href('example', {exampleNumber: section.prev.examples[nr].id});
                }
                return $state.href('section', {sectionNumber: section.prev.id});
            }

            return "#/";
        }

        function has_next() {
            return (angular.isObject(example) && angular.isObject(example.next)) ||
                (angular.isObject(section) && angular.isObject(section.next));
        }

        function has_previous() {
            return angular.isObject(section) && angular.isObject(example);
        }

        function set_examples(data) {
            var secprev = null;
            for (var i=0; i < data.length; i++) {
                var sec = data[i];

                sec.prev = secprev;
                if (angular.isObject(secprev)) {
                    secprev.next = sec;
                }
                secprev = sec;

                var exprev = null;
                for (var j=0; j < sec.examples.length; j++) {
                    var ex = sec.examples[j];
                    ex.prev = exprev;
                    ex.section = sec;
                    if (angular.isObject(exprev)) {
                        exprev.next = ex;
                    }
                    exprev = ex;
                }
            }
            examples = data;
        }
    }

    function menu() {
        var openedsection = null;
        var service = {
            selectSection: function(section) {
                openedsection = angular.isDefined(section) ? section: null;
            },
            toggleSelectSection: function(section) {
                openedsection = (openedsection === section ? null : section);
            },
            isSectionSelected: is_section_selected,
            getSectionName: get_section_name,
            getTitle: get_title,
        };
        return service;

        function get_title() {
            var title = 'Angular Tutorial';
            var sectionname = get_section_name();
            if (sectionname) {
                title = title + ' - ' + sectionname;

            }
            return title;
        }

        function is_section_selected(section) {
            return angular.isObject(openedsection) && openedsection.id === section.id;
        }

        function get_section_name() {
            if (!openedsection) {
                return "";
            }
            return openedsection.name;
        }
    }

    at_hljs.$inject = ['conf'];

    function at_hljs(conf) {
        return {
            scope: { file: '=', example: '=' },
            restrict: 'A',
            controller: function($scope, $http) {
                var path = [
                    conf.examplesPath,
                    $scope.example.id,
                    $scope.file
                ].join('/');
                $http.get(path).success(function(data) {
                    if (typeof(data) === 'object') {
                        // un-parse auto-parsed JSON files for presentation as text
                        data = JSON.stringify(data, null, 2);
                    } else {
                        // Remove trailing newlines from code presentation
                        data = data.trim();
                    }
                    $scope.content = data;
                });
            },
            link: function(scope, element, attrs) {
                var contentParent = angular.element(
                    '

'
                );
                element.append(contentParent);
                scope.$watch('content', function(data) {
                    if (data) {
                        var codeElement = contentParent.find('code');
                        var highlightedCode = hljs.highlightAuto(data);
                        codeElement.append(
                            highlightedCode.value).addClass('highlight');
                    }
                });
            }
        };
    }

    function hl() {
        var directive = {
            restrict: 'A',
            link: link,
        };
        return directive;

        function link(scope, element, attrs) {
            var d = element.html();
            var contentParent = angular.element(
                '

'
            );
            var codeElement = contentParent.find('code');

            element.empty();
            element.append(contentParent);

            var language = element.attr('hl');
            var highlightedCode;
            if (language) {
                highlightedCode = hljs.highlightAuto(d, [language]);
            }
            else {
                highlightedCode = hljs.highlightAuto(d);
            }
            codeElement.html(highlightedCode.value);
        }
    }
})();
<commit_msg>Differentiate between opened and selected sections<commit_after>/*
 * Copyright 2014 - 2015 Björn Ricks <bjoern.ricks@gmail.com>
 *
 */
(function() {
    'use strict';

    angular.module('angTutApp',
        ['ui.router', 'ngMaterial', 'angTutApp.controllers']
    )
    .config(app_config)
    .factory('conf', conf)
    .factory('examplesService', examplesService)
    .factory('menu', menu)
    .directive('hl', hl)
    .directive('atHljs', at_hljs);

    app_config.$inject = ['$stateProvider', '$urlRouterProvider'];

    function app_config($stateProvider, $urlRouterProvider) {
        $stateProvider.
            state('base', {
                    templateUrl: 'base.html',
                    abstract: true,
                    controller: 'BaseCtrl',
                    controllerAs: 'base',
            }).
            state('home', {
                    url: '/',
                    parent: 'base',
                    templateUrl: 'home.html',
                    controller: 'HomeCtrl',
            }).
            state('example', {
                    parent: 'base',
                    url: '/examples/:exampleNumber',
                    templateUrl: 'example-view.html',
                    controller: 'ExampleDetailCtrl',
                    controllerAs: 'ctrl',
            }).
            state('section', {
                    parent: 'base',
                    url: '/sections/:sectionNumber',
                    templateUrl: 'section-view.html',
                    controller: 'SectionCtrl',
                    controllerAs: 'ctrl',
            });
        $urlRouterProvider.otherwise('/');
    }

    function conf() {
        var service = {
            examplesPath: 'examples/',
        };
        return service;
    }

    examplesService.$inject = ['$http', '$q', '$state'];

    function examplesService($http, $q, $state) {

        var section = null;
        var example = null;
        var examples = null;

        var service = {
            get: get_examples,
            setSection: set_section,
            setExample: set_example,
            getNext: get_next,
            hasNext: has_next,
            hasPrevious: has_previous,
            getPrevious: get_previous,
        };

        return service;

        function set_section(sectionid) {
            return get_examples().then(function(data) {
                for(var i=0; i < data.length; i++) {
                    var sec = data[i];
                    if (sec.id == sectionid) {
                        section = sec;
                        example = null;
                        return section;
                    }
                }
            });
        }

        function set_example(exampleid) {
            return get_examples().then(function(data) {
                for(var i=0; i < data.length; i++) {
                    var sec = data[i];
                    for(var j=0; j < sec.examples.length; j++) {
                        var ex = sec.examples[j];
                        if (ex.id == exampleid) {
                            section = sec;
                            example = ex;
                            return example;
                        }
                    }
                }
            });
        }

        function get_examples() {
            var deferred = $q.defer();

            if (examples !== null) {
                deferred.resolve(examples);
            }
            else {
                $http.get('examples.json', {cache: true}).then(
                    function(response) {
                        set_examples(response.data);
                        deferred.resolve(examples);
                    },
                    function(response) {
                        deferred.reject(response);
                    }
                );
            }

            return deferred.promise;
        }

        function get_next() {
            if (angular.isObject(example)) {
                if (angular.isObject(example.next)) {
                    return $state.href('example', {exampleNumber: example.next.id});
                }
                if (angular.isObject(section) && angular.isObject(section.next)) {
                    return $state.href('section', {sectionNumber: section.next.id});
                }
            }

            if (angular.isObject(section)) {
                if (section.examples.length > 0) {
                    return $state.href('example', {exampleNumber: section.examples[0].id});
                }
                if (angular.isObject(section.next)) {
                    return $state.href('section', {sectionNumber: section.next.id});
                }
            }

            return "#/";
        }

        function get_previous() {
            if (angular.isObject(example)) {
                if (angular.isObject(example.prev)) {
                    return $state.href('example', {exampleNumber: example.prev.id});
                }
                if (angular.isObject(section) && angular.isObject(section.prev)) {
                    return $state.href('section', {sectionNumber: section.prev.id});
                }
            }

            if (angular.isObject(section) && angular.isObject(section.prev)) {
                var nr = section.prev.examples.length - 1;
                if (nr > 0) {
                    return $state.href('example', {exampleNumber: section.prev.examples[nr].id});
                }
                return $state.href('section', {sectionNumber: section.prev.id});
            }

            return "#/";
        }

        function has_next() {
            return (angular.isObject(example) && angular.isObject(example.next)) ||
                (angular.isObject(section) && angular.isObject(section.next));
        }

        function has_previous() {
            return angular.isObject(section) && angular.isObject(example);
        }

        function set_examples(data) {
            var secprev = null;
            for (var i=0; i < data.length; i++) {
                var sec = data[i];

                sec.prev = secprev;
                if (angular.isObject(secprev)) {
                    secprev.next = sec;
                }
                secprev = sec;

                var exprev = null;
                for (var j=0; j < sec.examples.length; j++) {
                    var ex = sec.examples[j];
                    ex.prev = exprev;
                    ex.section = sec;
                    if (angular.isObject(exprev)) {
                        exprev.next = ex;
                    }
                    exprev = ex;
                }
            }
            examples = data;
        }
    }

    function menu() {
        var openedsection = null;
        var selectedsection = null;
        var service = {
            selectSection: function(section) {
                selectedsection = angular.isDefined(section) ? section: null;
                openedsection = selectedsection;
            },
            toggleSelectSection: function(section) {
                openedsection = (openedsection === section ? null : section);
            },
            isSectionOpened: is_section_opened,
            isSectionSelected: is_section_selected,
            getSectionName: get_section_name,
            getTitle: get_title,
        };
        return service;

        function get_title() {
            var title = 'Angular Tutorial';
            var sectionname = get_section_name();
            if (sectionname) {
                title = title + ' - ' + sectionname;

            }
            return title;
        }

        function is_section_opened(section) {
            return angular.isObject(openedsection) && openedsection.id === section.id;
        }

        function is_section_selected(section) {
            return angular.isObject(selectedsection) && selectedsection.id === section.id;
        }

        function get_section_name() {
            if (!selectedsection) {
                return "";
            }
            return selectedsection.name;
        }
    }

    at_hljs.$inject = ['conf'];

    function at_hljs(conf) {
        return {
            scope: { file: '=', example: '=' },
            restrict: 'A',
            controller: function($scope, $http) {
                var path = [
                    conf.examplesPath,
                    $scope.example.id,
                    $scope.file
                ].join('/');
                $http.get(path).success(function(data) {
                    if (typeof(data) === 'object') {
                        // un-parse auto-parsed JSON files for presentation as text
                        data = JSON.stringify(data, null, 2);
                    } else {
                        // Remove trailing newlines from code presentation
                        data = data.trim();
                    }
                    $scope.content = data;
                });
            },
            link: function(scope, element, attrs) {
                var contentParent = angular.element(
                    '

'
                );
                element.append(contentParent);
                scope.$watch('content', function(data) {
                    if (data) {
                        var codeElement = contentParent.find('code');
                        var highlightedCode = hljs.highlightAuto(data);
                        codeElement.append(
                            highlightedCode.value).addClass('highlight');
                    }
                });
            }
        };
    }

    function hl() {
        var directive = {
            restrict: 'A',
            link: link,
        };
        return directive;

        function link(scope, element, attrs) {
            var d = element.html();
            var contentParent = angular.element(
                '

'
            );
            var codeElement = contentParent.find('code');

            element.empty();
            element.append(contentParent);

            var language = element.attr('hl');
            var highlightedCode;
            if (language) {
                highlightedCode = hljs.highlightAuto(d, [language]);
            }
            else {
                highlightedCode = hljs.highlightAuto(d);
            }
            codeElement.html(highlightedCode.value);
        }
    }
})();
<|endoftext|>


<commit_before><commit_msg>allow special characters like +<commit_after><|endoftext|>


<commit_before>// Copyright 2018, University of Colorado Boulder

/**
 * ?fuzzBoard keyboard fuzzer
 * TODO: keep track of keystate so that we don't trigger a keydown of keyA before the previous keyA keyup event has been called.
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const AccessibilityUtil = require( 'SCENERY/accessibility/AccessibilityUtil' );
  const KeyStateTracker = require( 'SCENERY/accessibility/KeyStateTracker' );
  const KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  const Random = require( 'DOT/Random' );
  const scenery = require( 'SCENERY/scenery' );
  const timer = require( 'PHET_CORE/timer' );

  // uppercase matters
  const keyboardTestingSchema = {
    INPUT: [ ...KeyboardUtil.ARROW_KEYS, KeyboardUtil.KEY_PAGE_UP, KeyboardUtil.KEY_PAGE_DOWN,
      KeyboardUtil.KEY_HOME, KeyboardUtil.KEY_END, KeyboardUtil.KEY_ENTER, KeyboardUtil.KEY_SPACE ],
    DIV: [ ...KeyboardUtil.ARROW_KEYS ],
    P: [ KeyboardUtil.KEY_ESCAPE ],
    BUTTON: [ KeyboardUtil.KEY_ENTER, KeyboardUtil.KEY_SPACE ]
  };

  const MAX_MS_KEY_HOLD_DOWN = 200;
  const NEXT_ELEMENT_THRESHOLD = .1;

  const DO_KNOWN_KEYS_THRESHOLD = .60; // for keydown/up
  const CLICK_EVENT = .10; // TODO because of implementation this is actually 4%. but reads like "10% of the time after 60% of the time"

  const KEY_DOWN = 'keydown';
  const KEY_UP = 'keyup';

  var min = 9;
  var max = 223;

  /**
   *
   * @param {Object} [options]
   * @constructor
   */
  class KeyboardFuzzer {
    constructor( display, seed ) {

      // @private
      this.display = display;
      this.random = new Random( { seed: seed } );
      this.keyStateTracker = new KeyStateTracker();
      this.keysPressedEachFrame = 100;
      this.keyupListeners = [];
      this.guaranteeNewUpdate = false; // when true, `chooseNextElement` will 100% give a new focused element.

      // @private {HTMLElement}
      this.currentElement = null;
    }

    /**
     * @private
     * Randomly decide if we should focus the next element, or stay focused on the current element
     */
    chooseNextElement() {
      if ( this.currentElement === null ) {
        this.currentElement = document.activeElement;
      }
      else if ( this.guaranteeNewUpdate || this.random.nextDouble() < NEXT_ELEMENT_THRESHOLD ) {

        // before we change focus to the next item, press "keyup" on all elements we had.
        this.clearListeners();
        var nextFocusable = AccessibilityUtil.getRandomFocusable();
        nextFocusable.focus();
        this.currentElement = nextFocusable;
        this.guaranteeNewUpdate = false;
      }
    }

    /**
     * @private
     */
    clearListeners() {
      this.keyupListeners.forEach( function( listener ) {
        timer.clearTimeout( listener );
        listener();
      } );
    }

    /**
     * @private
     * @param {HTMLElement} element
     */
    triggerClickEvent( element ) {
      element.click();
    }

    /**
     * Trigger a keydown/keyup pair. The keyup is triggered with a timeout
     * @param {HTMLElement} element
     * @param {number} keyCode
     */
    triggerKeyDownUpEvents( element, keyCode ) {

      if ( !this.keyStateTracker.isKeyDown( keyCode ) ) {

        // TODO: screen readers normally take our keydown events, but may not here, is the descrpency ok?
        this.triggerDOMEvent( KEY_DOWN, element, keyCode );

        this.keyupListeners.push( timer.setTimeout( () => {
          this.triggerDOMEvent( KEY_UP, element, keyCode );

        }, this.random.nextInt( MAX_MS_KEY_HOLD_DOWN ) ) );
      }
    }

    /**
     * Trigger a keydown/keyup pair with a random keyCode
     * @private
     * @param {HTMLElement} element
     */
    triggerRandomKeyDownUpEvents( element ) {

      var randomKeyCode = Math.floor( this.random.nextDouble() * ( max - min ) + min );

      this.triggerKeyDownUpEvents( element, randomKeyCode );

    }

    /**
     * A random event creater that sends keyboard events. Based on the idea of fuzzMouse, but to test/spam accessibility
     * related keyboard navigation and alternate input implementation.
     *
     * @public
     * TODO: NOTE: Right now this is a very experimental implementation. Tread wearily
     * TODO: @param keyboardPressesPerFocusedItem {number} - basically would be the same as fuzzRate, but handling
     * TODO:     the keydown events for a focused item
     */
    fuzzBoardEvents() {

      this.chooseNextElement();

      var elementWithFocus = document.activeElement;

      for ( let i = 0; i < this.keysPressedEachFrame; i++ ) {
        if ( elementWithFocus !== document.activeElement ) {
          this.guaranteeNewUpdate = true;
          break;
        }

        if ( keyboardTestingSchema[ elementWithFocus.tagName ] ) {

          if ( this.random.nextDouble() < DO_KNOWN_KEYS_THRESHOLD ) {
            const keyCodes = keyboardTestingSchema[ elementWithFocus.tagName ];
            const keyCode = this.random.sample( keyCodes );
            this.triggerKeyDownUpEvents( elementWithFocus, keyCode );
          }
          else if ( this.random.nextDouble() < CLICK_EVENT ) {
            this.triggerClickEvent( elementWithFocus );
          }
          else {
            this.triggerRandomKeyDownUpEvents( elementWithFocus );
          }
        }
        // TODO: What about other types of events, not just keydown/keyup??!?!
        // TODO: what about application role elements
      }
    }

    /**
     * Taken from example in http://output.jsbin.com/awenaq/3,
     * @param {string} event
     * @param {HTMLElement} element
     * @param {number} [keycode]
     * @private
     */
    triggerDOMEvent( event, element, keyCode ) {
      var eventObj = document.createEventObject ?
                     document.createEventObject() : document.createEvent( 'Events' );

      if ( eventObj.initEvent ) {
        eventObj.initEvent( event, true, true );
      }

      eventObj.keyCode = keyCode;
      // eventObj.shiftKey = true; // TODO: we can add modifier keys in here with options?
      eventObj.which = keyCode;

      if ( this.keyStateTracker.shiftKeyDown ) {
        eventObj.shiftKey = true;
      }

      if ( event === KEY_DOWN ) {
        this.keyStateTracker.keydownUpdate( eventObj );
      }
      else if ( event === KEY_UP ) {
        this.keyStateTracker.keyupUpdate( eventObj );
      }

      element.dispatchEvent ? element.dispatchEvent( eventObj ) : element.fireEvent( 'on' + event, eventObj );
    }
  }

  return scenery.register( 'KeyboardFuzzer', KeyboardFuzzer );
} );<commit_msg>update reference to active element for 871<commit_after>// Copyright 2018, University of Colorado Boulder

/**
 * ?fuzzBoard keyboard fuzzer
 * TODO: keep track of keystate so that we don't trigger a keydown of keyA before the previous keyA keyup event has been called.
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const AccessibilityUtil = require( 'SCENERY/accessibility/AccessibilityUtil' );
  const KeyStateTracker = require( 'SCENERY/accessibility/KeyStateTracker' );
  const KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  const Random = require( 'DOT/Random' );
  const scenery = require( 'SCENERY/scenery' );
  const timer = require( 'PHET_CORE/timer' );

  // uppercase matters
  const keyboardTestingSchema = {
    INPUT: [ ...KeyboardUtil.ARROW_KEYS, KeyboardUtil.KEY_PAGE_UP, KeyboardUtil.KEY_PAGE_DOWN,
      KeyboardUtil.KEY_HOME, KeyboardUtil.KEY_END, KeyboardUtil.KEY_ENTER, KeyboardUtil.KEY_SPACE ],
    DIV: [ ...KeyboardUtil.ARROW_KEYS ],
    P: [ KeyboardUtil.KEY_ESCAPE ],
    BUTTON: [ KeyboardUtil.KEY_ENTER, KeyboardUtil.KEY_SPACE ]
  };

  const MAX_MS_KEY_HOLD_DOWN = 200;
  const NEXT_ELEMENT_THRESHOLD = .1;

  const DO_KNOWN_KEYS_THRESHOLD = .60; // for keydown/up
  const CLICK_EVENT = .10; // TODO because of implementation this is actually 4%. but reads like "10% of the time after 60% of the time"

  const KEY_DOWN = 'keydown';
  const KEY_UP = 'keyup';

  var min = 9;
  var max = 223;

  /**
   *
   * @param {Object} [options]
   * @constructor
   */
  class KeyboardFuzzer {
    constructor( display, seed ) {

      // @private
      this.display = display;
      this.random = new Random( { seed: seed } );
      this.keyStateTracker = new KeyStateTracker();
      this.keysPressedEachFrame = 100;
      this.keyupListeners = [];

      // @private {HTMLElement}
      this.currentElement = null;
    }

    /**
     * @private
     * Randomly decide if we should focus the next element, or stay focused on the current element
     */
    chooseNextElement() {
      if ( this.currentElement === null ) {
        this.currentElement = document.activeElement;
      }
      else if ( this.random.nextDouble() < NEXT_ELEMENT_THRESHOLD ) {

        // before we change focus to the next item, immediately release all keys that were down on the active element
        this.clearListeners();
        var nextFocusable = AccessibilityUtil.getRandomFocusable();
        nextFocusable.focus();
        this.currentElement = nextFocusable;
      }
    }

    /**
     * @private
     */
    clearListeners() {
      this.keyupListeners.forEach( function( listener ) {
        timer.clearTimeout( listener );
        listener();
      } );
    }

    /**
     * @private
     * @param {HTMLElement} element
     */
    triggerClickEvent( element ) {
      element.click();
    }

    /**
     * Trigger a keydown/keyup pair. The keyup is triggered with a timeout
     * @param {HTMLElement} element
     * @param {number} keyCode
     */
    triggerKeyDownUpEvents( element, keyCode ) {

      if ( !this.keyStateTracker.isKeyDown( keyCode ) ) {

        // TODO: screen readers normally take our keydown events, but may not here, is the descrpency ok?
        this.triggerDOMEvent( KEY_DOWN, element, keyCode );

        this.keyupListeners.push( timer.setTimeout( () => {
          this.triggerDOMEvent( KEY_UP, element, keyCode );

        }, this.random.nextInt( MAX_MS_KEY_HOLD_DOWN ) ) );
      }
    }

    /**
     * Trigger a keydown/keyup pair with a random keyCode
     * @private
     * @param {HTMLElement} element
     */
    triggerRandomKeyDownUpEvents( element ) {

      var randomKeyCode = Math.floor( this.random.nextDouble() * ( max - min ) + min );

      this.triggerKeyDownUpEvents( element, randomKeyCode );

    }

    /**
     * A random event creater that sends keyboard events. Based on the idea of fuzzMouse, but to test/spam accessibility
     * related keyboard navigation and alternate input implementation.
     *
     * @public
     * TODO: NOTE: Right now this is a very experimental implementation. Tread wearily
     * TODO: @param keyboardPressesPerFocusedItem {number} - basically would be the same as fuzzRate, but handling
     * TODO:     the keydown events for a focused item
     */
    fuzzBoardEvents() {

      this.chooseNextElement();

      for ( let i = 0; i < this.keysPressedEachFrame; i++ ) {

        // get active element, focus might have changed in the last press
        var elementWithFocus = document.activeElement;

        if ( keyboardTestingSchema[ elementWithFocus.tagName ] ) {

          if ( this.random.nextDouble() < DO_KNOWN_KEYS_THRESHOLD ) {
            const keyCodes = keyboardTestingSchema[ elementWithFocus.tagName ];
            const keyCode = this.random.sample( keyCodes );
            this.triggerKeyDownUpEvents( elementWithFocus, keyCode );
          }
          else if ( this.random.nextDouble() < CLICK_EVENT ) {
            this.triggerClickEvent( elementWithFocus );
          }
          else {
            this.triggerRandomKeyDownUpEvents( elementWithFocus );
          }
        }
        // TODO: What about other types of events, not just keydown/keyup??!?!
        // TODO: what about application role elements
      }
    }

    /**
     * Taken from example in http://output.jsbin.com/awenaq/3,
     * @param {string} event
     * @param {HTMLElement} element
     * @param {number} [keycode]
     * @private
     */
    triggerDOMEvent( event, element, keyCode ) {
      var eventObj = document.createEventObject ?
                     document.createEventObject() : document.createEvent( 'Events' );

      if ( eventObj.initEvent ) {
        eventObj.initEvent( event, true, true );
      }

      eventObj.keyCode = keyCode;
      // eventObj.shiftKey = true; // TODO: we can add modifier keys in here with options?
      eventObj.which = keyCode;

      if ( this.keyStateTracker.shiftKeyDown ) {
        eventObj.shiftKey = true;
      }

      if ( event === KEY_DOWN ) {
        this.keyStateTracker.keydownUpdate( eventObj );
      }
      else if ( event === KEY_UP ) {
        this.keyStateTracker.keyupUpdate( eventObj );
      }

      element.dispatchEvent ? element.dispatchEvent( eventObj ) : element.fireEvent( 'on' + event, eventObj );
    }
  }

  return scenery.register( 'KeyboardFuzzer', KeyboardFuzzer );
} );<|endoftext|>


<commit_before>var React = require("react");
var Router = require("react-router");
var Link = Router.Link;
var JournalActions = require("../actions/JournalActions");
var Loading = require('./Loading.react.js');
var JournalFormMixin = require("../mixins/JournalFormMixin");
var JournalStore = require("../stores/JournalStore");

function getJournalEditState (id, errors) {
  var entry = JournalStore.findById(id);
  return {
    id: entry.id,
    title: entry.title,
    markdown_content: entry.markdown_content,
    updated: entry.updated,
    errors: errors
  };
}

var JournalEdit = React.createClass({
  mixins: [JournalFormMixin, Router.Navigation, Router.State],
  componentDidMount: function () {
    JournalStore.addChangeListener(this._onChange);
    var id = this.getParams().id;
    JournalActions.getEntryInformation(id);
  },

  componentWillUnmount: function () {
    JournalStore.removeChangeListener(this._onChange);
  },

  getInitialState: function () {
    return {
      id: null,
      title: null,
      markdown_content: null,
      updated: false,
      errors: null
    };
  },

  _onChange: function () {
    var errors = JournalStore.getErrors();
    this.setState(getJournalEditState(JournalStore.getSetId(), errors));
    if (errors === null && this.state.updated) {
      this.transitionTo("/journal");
    }
  },

  render: function () {
    var messages = this.getErrorMessages(this.state);
    return (

Edit Journal Entry

          <Link to="home">Home</Link>
          <Link to="journal">Journal</Link>
          Edit

        <form action="/journal" method="post" onSubmit={this.updateEntry}>

            Title
            <input className="textField" type="text" name="title" onChange={this.handleChange.call(this, "title")} value={this.state.title} />
            <Error message={messages['title']} />

            Content
            <textarea id="markdown_content" value={this.state.markdown_content} rows="4" onChange={this.handleChange.call(this, "markdown_content")} />
            <Error message={messages['markdown_content']} />

            <input type="submit" />

        </form>

    );
  },

  updateEntry: function (e) {
    e.preventDefault();
    JournalActions.updateEntry(this.state);
  }
});

module.exports = JournalEdit;<commit_msg>empty did update<commit_after>var React = require("react");
var Router = require("react-router");
var Link = Router.Link;
var JournalActions = require("../actions/JournalActions");
var Loading = require('./Loading.react.js');
var JournalFormMixin = require("../mixins/JournalFormMixin");
var JournalStore = require("../stores/JournalStore");

function getJournalEditState (id, errors) {
  var entry = JournalStore.findById(id);
  return {
    id: entry.id,
    title: entry.title,
    markdown_content: entry.markdown_content,
    updated: entry.updated,
    errors: errors
  };
}

var JournalEdit = React.createClass({
  mixins: [JournalFormMixin, Router.Navigation, Router.State],
  componentDidMount: function () {
    JournalStore.addChangeListener(this._onChange);
    var id = this.getParams().id;
    JournalActions.getEntryInformation(id);
  },

  componentDidUpdate: function(prevProps, prevState) {

  },

  componentWillUnmount: function () {
    JournalStore.removeChangeListener(this._onChange);
  },

  getInitialState: function () {
    return {
      id: null,
      title: null,
      markdown_content: null,
      updated: false,
      errors: null
    };
  },

  _onChange: function () {
    var errors = JournalStore.getErrors();
    this.setState(getJournalEditState(JournalStore.getSetId(), errors));
    if (errors === null && this.state.updated) {
      this.transitionTo("/journal");
    }
  },

  render: function () {
    var messages = this.getErrorMessages(this.state);
    return (

Edit Journal Entry

          <Link to="home">Home</Link>
          <Link to="journal">Journal</Link>
          Edit

        <form action="/journal" method="post" onSubmit={this.updateEntry}>

            Title
            <input className="textField" type="text" name="title" onChange={this.handleChange.call(this, "title")} value={this.state.title} />
            <Error message={messages['title']} />

            Content
            <textarea id="markdown_content" value={this.state.markdown_content} rows="4" onChange={this.handleChange.call(this, "markdown_content")} />
            <Error message={messages['markdown_content']} />

            <input type="submit" />

        </form>

    );
  },

  updateEntry: function (e) {
    e.preventDefault();
    JournalActions.updateEntry(this.state);
  }
});

module.exports = JournalEdit;<|endoftext|>


<commit_before>function scrollTimelineToAppropriateDate() {
    var highlight_el = $('.timeline.highlight a[data-date="'+$('#article').data('article-previous-date')+'"]');
    var version_el = $('.timeline.version a[data-date="'+$('#article').data('article-version-date')+'"]');
    if(version_el.length>0 && version_el.offset().left!=0) {
        $('.timeline.version').scrollTo(version_el, {'over': -33});
    }
    if(highlight_el.length>0 && highlight_el.offset().left!=0) {
        $('.timeline.highlight').scrollTo(highlight_el, {'over': -33});
    }
}

function initMarkAsRead() {

    var mark_as_read_if_needed = function() {
        $('#mark_as_read .tracking-status').each(function(){
            if($(this).data('track') == 'yes') {
                var element = $(this);
                var f = function() {
                    element.load($('#article').data('mark-as-read'), function() { });
                };
                setTimeout(f, 2500);
            }
        });
    };

    $('#end_of_article').waypoint(function() {
        if($('#article').data('article-current') == 'False' || $('#article').data('article-changed') == 'False')
            return;
        $('#mark_as_read').removeClass('hiding');
        mark_as_read_if_needed();
    }, {'offset': '100%'});
    var eh = function(event) {
        event.preventDefault();
        event.stopPropagation();
        var element = $(this).parent('.reload-here');
        $(this).parent('.reload-here').addClass('loading');
        $(this).parent('.reload-here').load($(this).attr('href'), function() { mark_as_read_if_needed(); element.removeClass('loading'); } );
    };
    $('#mark_as_read .tracking-status').on('click', 'a', eh);

    $('#mark_as_read').on('click', 'a', eh);
}

$(document).ready(initMarkAsRead);

initTimelines_done = false;
function initTimelines() {
    if(initTimelines_done) {
        scrollTimelineToAppropriateDate();
        return;
    }
    initTimelines_done = true;
    var width = $('.timeline li .month').parent().map(
            function() {
                return $(this).offset()['left']+$(this).outerWidth() - $(this).parents('.timeline').offset()['left'] + 1;
           }).toArray().reduce(function(x,y){return (x>y?x:y);});
    $('.timeline .months').attr('style', 'width: '+width+'px;');

    var highlight_scroll = $('#article').data('article-previous-date');
    var version_scroll = $('#article').data('article-version-date');

    if(highlight_scroll) {
        var highlight_el = $('.timeline.highlight a[data-date="'+highlight_scroll+'"]');
        highlight_el.addClass('mark');
    }
    if(version_scroll) {
        var version_el = $('.timeline.version a[data-date="'+version_scroll+'"]');
        version_el.addClass('mark');
    }
    scrollTimelineToAppropriateDate();
}

function initHistoryView() {
    $('.historyform a.switch').click(function() {
        var which = $(this).hasClass('version')?'version':'highlight';
        $(this).closest('.historyform').each(function(i,e){
            $(e).attr('class','historyform '+($(e).hasClass(which)?'':which));
        });
        setTimeout(initTimelines, 0);
    });
}

$(document).ready(initHistoryView);
<commit_msg>also scroll in version timeline<commit_after>function scrollTimelineToAppropriateDate() {
    var highlight_el = $('.timeline.highlight a[data-date="'+$('#article').data('article-previous-date')+'"]');
    var version_el = $('.timeline.version a[data-date="'+$('#article').data('article-current-date')+'"]');
    if(version_el.length>0 && version_el.offset().left!=0) {
        $('.timeline.version').scrollTo(version_el, {'over': -33});
    }
    if(highlight_el.length>0 && highlight_el.offset().left!=0) {
        $('.timeline.highlight').scrollTo(highlight_el, {'over': -33});
    }
}

function initMarkAsRead() {

    var mark_as_read_if_needed = function() {
        $('#mark_as_read .tracking-status').each(function(){
            if($(this).data('track') == 'yes') {
                var element = $(this);
                var f = function() {
                    element.load($('#article').data('mark-as-read'), function() { });
                };
                setTimeout(f, 2500);
            }
        });
    };

    $('#end_of_article').waypoint(function() {
        if($('#article').data('article-current') == 'False' || $('#article').data('article-changed') == 'False')
            return;
        $('#mark_as_read').removeClass('hiding');
        mark_as_read_if_needed();
    }, {'offset': '100%'});
    var eh = function(event) {
        event.preventDefault();
        event.stopPropagation();
        var element = $(this).parent('.reload-here');
        $(this).parent('.reload-here').addClass('loading');
        $(this).parent('.reload-here').load($(this).attr('href'), function() { mark_as_read_if_needed(); element.removeClass('loading'); } );
    };
    $('#mark_as_read .tracking-status').on('click', 'a', eh);

    $('#mark_as_read').on('click', 'a', eh);
}

$(document).ready(initMarkAsRead);

initTimelines_done = false;
function initTimelines() {
    if(initTimelines_done) {
        scrollTimelineToAppropriateDate();
        return;
    }
    initTimelines_done = true;
    var width = $('.timeline li .month').parent().map(
            function() {
                return $(this).offset()['left']+$(this).outerWidth() - $(this).parents('.timeline').offset()['left'] + 1;
           }).toArray().reduce(function(x,y){return (x>y?x:y);});
    $('.timeline .months').attr('style', 'width: '+width+'px;');

    var highlight_scroll = $('#article').data('article-previous-date');
    var version_scroll = $('#article').data('article-version-date');

    if(highlight_scroll) {
        var highlight_el = $('.timeline.highlight a[data-date="'+highlight_scroll+'"]');
        highlight_el.addClass('mark');
    }
    if(version_scroll) {
        var version_el = $('.timeline.version a[data-date="'+version_scroll+'"]');
        version_el.addClass('mark');
    }
    scrollTimelineToAppropriateDate();
}

function initHistoryView() {
    $('.historyform a.switch').click(function() {
        var which = $(this).hasClass('version')?'version':'highlight';
        $(this).closest('.historyform').each(function(i,e){
            $(e).attr('class','historyform '+($(e).hasClass(which)?'':which));
        });
        setTimeout(initTimelines, 0);
    });
}

$(document).ready(initHistoryView);
<|endoftext|>


<commit_before>// A generalisation of the search-and-sort functionality that
// underlies ui-users, ui-items, etc.

import React from 'react';
import PropTypes from 'prop-types';
import Route from 'react-router-dom/Route';
import { withRouter } from 'react-router'
import queryString from 'query-string';
import { stripesShape } from '@folio/stripes-core/src/Stripes';
import FilterGroups, { initialFilterState, onChangeFilter as commonChangeFilter } from '@folio/stripes-components/lib/FilterGroups';
import FilterPaneSearch from '@folio/stripes-components/lib/FilterPaneSearch';
import IfPermission from '@folio/stripes-components/lib/IfPermission';
import Pane from '@folio/stripes-components/lib/Pane';
import Paneset from '@folio/stripes-components/lib/Paneset';
import PaneMenu from '@folio/stripes-components/lib/PaneMenu';
import Button from '@folio/stripes-components/lib/Button';
import MultiColumnList from '@folio/stripes-components/lib/MultiColumnList';
import Layer from '@folio/stripes-components/lib/Layer';
import SRStatus from '@folio/stripes-components/lib/SRStatus';

class SearchAndSort extends React.Component {
  static propTypes = {
    stripes: stripesShape.isRequired,
    parentResources: PropTypes.shape({
      patronGroups: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
      addressTypes: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
      users: PropTypes.shape({
        hasLoaded: PropTypes.bool.isRequired,
        other: PropTypes.shape({
          totalRecords: PropTypes.number.isRequired,
        }),
        isPending: PropTypes.bool.isPending,
        successfulMutations: PropTypes.arrayOf(
          PropTypes.shape({
            record: PropTypes.shape({
              id: PropTypes.string.isRequired,
              username: PropTypes.string.isRequired,
            }).isRequired,
          }),
        ),
      }),
      userCount: PropTypes.number,
    }).isRequired,
    resources: PropTypes.shape({
      notes: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
    }).isRequired,
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
    }).isRequired,
    match: PropTypes.shape({
      path: PropTypes.string.isRequired,
    }).isRequired,
    mutator: PropTypes.shape({
      userCount: PropTypes.shape({
        replace: PropTypes.func,
      }),
      users: PropTypes.shape({
        POST: PropTypes.func,
      }),
    }).isRequired,
    okapi: PropTypes.shape({
      url: PropTypes.string.isRequired,
      tenant: PropTypes.string.isRequired,
      token: PropTypes.string.isRequired,
    }).isRequired,
    onSelectRow: PropTypes.func,
    disableUserCreation: PropTypes.bool,
  };

  constructor(props) {
    super(props);

    const urlQuery = this.props.urlQuery;

    let initiallySelected = {};
    if (/users\/view/.test(this.props.path)) {
      const id = /view\/(.*)$/.exec(this.props.path)[1];
      initiallySelected = { id };
    }

    this.state = {
      filters: initialFilterState(this.props.filterConfig, urlQuery.filters),
      selectedItem: initiallySelected,
      searchTerm: urlQuery.query || '',
      sortOrder: urlQuery.sort || '',
      showNotesPane: false,
    };

    this.commonChangeFilter = commonChangeFilter.bind(this);
  }

  onChangeFilter = (e) => {
    this.props.parentMutator.userCount.replace(INITIAL_RESULT_COUNT);
    this.commonChangeFilter(e);
  }

  updateFilters = (filters) => { // provided for onChangeFilter
    this.transitionToParams({ filters: Object.keys(filters).filter(key => filters[key]).join(',') });
  }

  render() {
    const { parentResources, stripes, urlQuery, filterConfig } = this.props;
    const users = (parentResources.users || {}).records || [];
    const patronGroups = (parentResources.patronGroups || {}).records || [];
    const addressTypes = (parentResources.addressTypes || {}).records || [];
    const resource = parentResources.users;

    /* searchHeader is a 'custom pane header' */
    const searchHeader = <FilterPaneSearch
      searchFieldId="input-user-search"
      onChange={this.onChangeSearch}
      onClear={this.onClearSearch}
      resultsList={this.resultsList}
      value={this.state.searchTerm}
      placeholder={stripes.intl.formatMessage({ id: 'ui-users.search' })} />;

    const newUserButton = (
      <IfPermission perm="users.item.post">
        <IfPermission perm="login.item.post">
          <IfPermission perm="perms.users.item.post">
            <PaneMenu>
              <Button id="clickable-newuser" title="Add New User" onClick={this.onClickAddNewUser} buttonStyle="primary paneHeaderNewButton">+ New</Button>
            </PaneMenu>
          </IfPermission>
        </IfPermission>
      </IfPermission>
    );

    const resultsFormatter = {
      Status: user => (user.active ? 'Active' : 'Inactive'),
      Name: user => getFullName(user),
      Barcode: user => user.barcode,
      'Patron Group': (user) => {
        const pg = patronGroups.filter(g => g.id === user.patronGroup)[0];
        return pg ? pg.group : '?';
      },
      Username: user => user.username,
      Email: user => _.get(user, ['personal', 'email']),
    };

    const detailsPane = (
      stripes.hasPerm('users.item.get') ?
        (<Route
          path={`${this.props.match.path}/view/:userid`}
          render={props => <this.connectedViewUser stripes={stripes} okapi={this.okapi} paneWidth="44%" onClose={this.collapseDetails} addressTypes={addressTypes} notesToggle={this.toggleNotes} {...props} />}
        />) :
        (

Permission Error

Sorry - your user permissions do not allow access to this page.

));

    const maybeTerm = this.state.searchTerm ? ` for "${this.state.searchTerm}"` : '';
    const maybeSpelling = this.state.searchTerm ? 'spelling and ' : '';
    const count = resource && resource.hasLoaded ? resource.other.totalRecords : '';
    return (
      <Paneset>
        <SRStatus ref={(ref) => { this.SRStatus = ref; }} />
        {/* Filter Pane */}
        <Pane id="pane-filter" defaultWidth="16%" header={searchHeader}>
          <FilterGroups config={filterConfig} filters={this.state.filters} onChangeFilter={this.onChangeFilter} />
        </Pane>
        {/* Results Pane */}
        <Pane
          id="pane-results"
          defaultWidth="fill"
          paneTitle={

              Users

                {stripes.intl.formatMessage({ id: 'ui-users.resultCount' }, { count })}

          }
          lastMenu={!this.props.disableUserCreation ? newUserButton : null}
          noOverflow
        >
          <MultiColumnList
            id="list-users"
            contentData={users}
            selectedRow={this.state.selectedItem}
            rowMetadata={['id', 'username']}
            formatter={resultsFormatter}
            onRowClick={this.onSelectRow}
            onHeaderClick={this.onSort}
            onNeedMoreData={this.onNeedMore}
            visibleColumns={['Status', 'Name', 'Barcode', 'Patron Group', 'Username', 'Email']}
            sortOrder={this.state.sortOrder.replace(/^-/, '').replace(/,.*/, '')}
            sortDirection={this.state.sortOrder.startsWith('-') ? 'descending' : 'ascending'}
            isEmptyMessage={`No results found${maybeTerm}. Please check your ${maybeSpelling}filters.`}
            columnMapping={{ Username: 'username' }}
            loading={resource ? resource.isPending : false}
            autosize
            virtualize
            ariaLabel={'User search results'}
            rowFormatter={this.anchoredRowFormatter}
            containerRef={(ref) => { this.resultsList = ref; }}
          />
        </Pane>

        {detailsPane}
        <Layer isOpen={urlQuery.layer ? urlQuery.layer === 'create' : false} label="Add New User Dialog">
          <UserForm
            id="userform-adduser"
            initialValues={{ active: true, personal: { preferredContactTypeId: '002' } }}
            addressTypes={addressTypes}
            onSubmit={(record) => { this.create(record); }}
            onCancel={this.onClickCloseNewUser}
            okapi={this.okapi}
            optionLists={{ patronGroups, contactTypes }}
          />
        </Layer>
        {
          this.state.showNotesPane &&
          <Route
            path={`${this.props.match.path}/view/:id`}
            render={props => <this.connectedNotes
              stripes={stripes}
              okapi={this.okapi}
              onToggle={this.toggleNotes}
              link={`users/${props.match.params.id}`}
              notesResource={this.props.resources.notes}
              usersResource={this.props.resources.users}
              {...props}
            />}
          />
          }
      </Paneset>
    );
  }
}

export default withRouter(SearchAndSort);
<commit_msg>Rolling<commit_after>// A generalisation of the search-and-sort functionality that
// underlies ui-users, ui-items, etc.

import React from 'react';
import PropTypes from 'prop-types';
import Route from 'react-router-dom/Route';
import { withRouter } from 'react-router'
import queryString from 'query-string';
import { stripesShape } from '@folio/stripes-core/src/Stripes';
import FilterGroups, { initialFilterState, onChangeFilter as commonChangeFilter } from '@folio/stripes-components/lib/FilterGroups';
import FilterPaneSearch from '@folio/stripes-components/lib/FilterPaneSearch';
import IfPermission from '@folio/stripes-components/lib/IfPermission';
import Pane from '@folio/stripes-components/lib/Pane';
import Paneset from '@folio/stripes-components/lib/Paneset';
import PaneMenu from '@folio/stripes-components/lib/PaneMenu';
import Button from '@folio/stripes-components/lib/Button';
import MultiColumnList from '@folio/stripes-components/lib/MultiColumnList';
import Layer from '@folio/stripes-components/lib/Layer';
import SRStatus from '@folio/stripes-components/lib/SRStatus';
import UserForm from '../../UserForm'; // XXX relocate this
import ViewUser from '../../ViewUser'; // XXX relocate this
import contactTypes from '../../data/contactTypes';

class SearchAndSort extends React.Component {
  static propTypes = {
    stripes: stripesShape.isRequired,
    parentResources: PropTypes.shape({
      patronGroups: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
      addressTypes: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
      users: PropTypes.shape({
        hasLoaded: PropTypes.bool.isRequired,
        other: PropTypes.shape({
          totalRecords: PropTypes.number.isRequired,
        }),
        isPending: PropTypes.bool.isPending,
        successfulMutations: PropTypes.arrayOf(
          PropTypes.shape({
            record: PropTypes.shape({
              id: PropTypes.string.isRequired,
              username: PropTypes.string.isRequired,
            }).isRequired,
          }),
        ),
      }),
      userCount: PropTypes.number,
    }).isRequired,
    resources: PropTypes.shape({
      notes: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
    }).isRequired,
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
    }).isRequired,
    match: PropTypes.shape({
      path: PropTypes.string.isRequired,
    }).isRequired,
    mutator: PropTypes.shape({
      userCount: PropTypes.shape({
        replace: PropTypes.func,
      }),
      users: PropTypes.shape({
        POST: PropTypes.func,
      }),
    }).isRequired,
    okapi: PropTypes.shape({
      url: PropTypes.string.isRequired,
      tenant: PropTypes.string.isRequired,
      token: PropTypes.string.isRequired,
    }).isRequired,
    onSelectRow: PropTypes.func,
    disableUserCreation: PropTypes.bool,
  };

  constructor(props) {
    super(props);

    const urlQuery = this.props.urlQuery;

    let initiallySelected = {};
    if (/users\/view/.test(this.props.path)) {
      const id = /view\/(.*)$/.exec(this.props.path)[1];
      initiallySelected = { id };
    }

    this.state = {
      filters: initialFilterState(this.props.filterConfig, urlQuery.filters),
      selectedItem: initiallySelected,
      searchTerm: urlQuery.query || '',
      sortOrder: urlQuery.sort || '',
      showNotesPane: false,
    };

    this.commonChangeFilter = commonChangeFilter.bind(this);
    this.connectedViewUser = props.stripes.connect(ViewUser);
  }

  onChangeFilter = (e) => {
    this.props.parentMutator.userCount.replace(INITIAL_RESULT_COUNT);
    this.commonChangeFilter(e);
  }

  updateFilters = (filters) => { // provided for onChangeFilter
    this.transitionToParams({ filters: Object.keys(filters).filter(key => filters[key]).join(',') });
  }

  render() {
    const { parentResources, stripes, urlQuery, filterConfig } = this.props;
    const users = (parentResources.users || {}).records || [];
    const patronGroups = (parentResources.patronGroups || {}).records || [];
    const addressTypes = (parentResources.addressTypes || {}).records || [];
    const resource = parentResources.users;

    /* searchHeader is a 'custom pane header' */
    const searchHeader = <FilterPaneSearch
      searchFieldId="input-user-search"
      onChange={this.onChangeSearch}
      onClear={this.onClearSearch}
      resultsList={this.resultsList}
      value={this.state.searchTerm}
      placeholder={stripes.intl.formatMessage({ id: 'ui-users.search' })} />;

    const newUserButton = (
      <IfPermission perm="users.item.post">
        <IfPermission perm="login.item.post">
          <IfPermission perm="perms.users.item.post">
            <PaneMenu>
              <Button id="clickable-newuser" title="Add New User" onClick={this.onClickAddNewUser} buttonStyle="primary paneHeaderNewButton">+ New</Button>
            </PaneMenu>
          </IfPermission>
        </IfPermission>
      </IfPermission>
    );

    const resultsFormatter = {
      Status: user => (user.active ? 'Active' : 'Inactive'),
      Name: user => getFullName(user),
      Barcode: user => user.barcode,
      'Patron Group': (user) => {
        const pg = patronGroups.filter(g => g.id === user.patronGroup)[0];
        return pg ? pg.group : '?';
      },
      Username: user => user.username,
      Email: user => _.get(user, ['personal', 'email']),
    };

    const detailsPane = (
      stripes.hasPerm('users.item.get') ?
        (<Route
          path={`${this.props.match.path}/view/:userid`}
          render={props => <this.connectedViewUser stripes={stripes} okapi={this.okapi} paneWidth="44%" onClose={this.collapseDetails} addressTypes={addressTypes} notesToggle={this.toggleNotes} {...props} />}
        />) :
        (

Permission Error

Sorry - your user permissions do not allow access to this page.

));

    const maybeTerm = this.state.searchTerm ? ` for "${this.state.searchTerm}"` : '';
    const maybeSpelling = this.state.searchTerm ? 'spelling and ' : '';
    const count = resource && resource.hasLoaded ? resource.other.totalRecords : '';
    return (
      <Paneset>
        <SRStatus ref={(ref) => { this.SRStatus = ref; }} />
        {/* Filter Pane */}
        <Pane id="pane-filter" defaultWidth="16%" header={searchHeader}>
          <FilterGroups config={filterConfig} filters={this.state.filters} onChangeFilter={this.onChangeFilter} />
        </Pane>
        {/* Results Pane */}
        <Pane
          id="pane-results"
          defaultWidth="fill"
          paneTitle={

              Users

                {stripes.intl.formatMessage({ id: 'ui-users.resultCount' }, { count })}

          }
          lastMenu={!this.props.disableUserCreation ? newUserButton : null}
          noOverflow
        >
          <MultiColumnList
            id="list-users"
            contentData={users}
            selectedRow={this.state.selectedItem}
            rowMetadata={['id', 'username']}
            formatter={resultsFormatter}
            onRowClick={this.onSelectRow}
            onHeaderClick={this.onSort}
            onNeedMoreData={this.onNeedMore}
            visibleColumns={['Status', 'Name', 'Barcode', 'Patron Group', 'Username', 'Email']}
            sortOrder={this.state.sortOrder.replace(/^-/, '').replace(/,.*/, '')}
            sortDirection={this.state.sortOrder.startsWith('-') ? 'descending' : 'ascending'}
            isEmptyMessage={`No results found${maybeTerm}. Please check your ${maybeSpelling}filters.`}
            columnMapping={{ Username: 'username' }}
            loading={resource ? resource.isPending : false}
            autosize
            virtualize
            ariaLabel={'User search results'}
            rowFormatter={this.anchoredRowFormatter}
            containerRef={(ref) => { this.resultsList = ref; }}
          />
        </Pane>

        {detailsPane}
        <Layer isOpen={urlQuery.layer ? urlQuery.layer === 'create' : false} label="Add New User Dialog">
          <UserForm
            id="userform-adduser"
            initialValues={{ active: true, personal: { preferredContactTypeId: '002' } }}
            addressTypes={addressTypes}
            onSubmit={(record) => { this.create(record); }}
            onCancel={this.onClickCloseNewUser}
            okapi={this.okapi}
            optionLists={{ patronGroups, contactTypes }}
          />
        </Layer>
        {
          this.state.showNotesPane &&
          <Route
            path={`${this.props.match.path}/view/:id`}
            render={props => <this.connectedNotes
              stripes={stripes}
              okapi={this.okapi}
              onToggle={this.toggleNotes}
              link={`users/${props.match.params.id}`}
              notesResource={this.props.resources.notes}
              usersResource={this.props.resources.users}
              {...props}
            />}
          />
          }
      </Paneset>
    );
  }
}

export default withRouter(SearchAndSort);
<|endoftext|>


<commit_before>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2016 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

YUI.add('deployment-cloud', function() {

  juju.components.DeploymentCloud = React.createClass({
    propTypes: {
      acl: React.PropTypes.object.isRequired,
      cloud: React.PropTypes.object,
      clouds: React.PropTypes.object.isRequired,
      listClouds: React.PropTypes.func.isRequired,
      setCloud: React.PropTypes.func.isRequired
    },

    getInitialState: function() {
      return {
        clouds: [],
        cloudsLoading: true
      };
    },

    componentWillMount: function() {
      this.props.listClouds((error, clouds) => {
        if (error) {
          console.error('Unable to list clouds', error);
          return;
        }
        const cloudList = [];
        if (clouds) {
          cloudList = Object.keys(clouds).map(id => {
            const cloud = clouds[id];
            cloud.id = id;
            return cloud;
          });
        }
        this.setState({
          clouds: cloudList,
          cloudsLoading: false
        });
      });
    },

    /**
      Generate the list of clouds.

      @method _generateClouds
      @returns {Object} The cloud list.
    */
    _generateClouds: function() {
      if (this.state.cloudsLoading) {
        return (

            <juju.components.Spinner />

);
      }
      if (this.props.cloud) {
        return;
      }
      var clouds = [];
      this.state.clouds.forEach((cloud, i) => {
        var classes = classNames(
          'deployment-cloud__cloud',
          'four-col',
          {'last-col': i % 3 === 2});
        clouds.push(

              {this._generateLogo(cloud)}

);
      });
      return (

          {clouds}

);
    },

    /**
      Generate the logo for the selected cloud.

      @method _generateCloud
      @returns {Object} The cloud.
    */
    _generateCloud: function() {
      var cloud = this.props.cloud;
      if (!cloud) {
        return;
      }
      return (

          {this._generateLogo(cloud)}

);
    },

    /**
      Generate the logo for a cloud;

      @method _generateLogo
      @param {Object} cloud A cloud.
      @returns {Array} The logo.
    */
    _generateLogo: function(cloud) {
      var info = this.props.clouds[cloud.id];
      if (!info) {
        return cloud.name;
      }
      const id = info.id.replace('cloud-', '');
      return info.showLogo ? (
        <juju.components.SvgIcon
        height={info.svgHeight}
        name={id}
        width={info.svgWidth} />) : info.title;
    },

    render: function() {
      return (

          {this._generateClouds()}
          {this._generateCloud()}

      );
    }

  });

}, '0.1.0', {
  requires: [
    'loading-spinner',
    'svg-icon'
  ]
});
<commit_msg>Select the first cloud if there is only one.<commit_after>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2016 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

YUI.add('deployment-cloud', function() {

  juju.components.DeploymentCloud = React.createClass({
    propTypes: {
      acl: React.PropTypes.object.isRequired,
      cloud: React.PropTypes.object,
      clouds: React.PropTypes.object.isRequired,
      listClouds: React.PropTypes.func.isRequired,
      setCloud: React.PropTypes.func.isRequired
    },

    getInitialState: function() {
      return {
        clouds: [],
        cloudsLoading: true
      };
    },

    componentWillMount: function() {
      this.props.listClouds((error, clouds) => {
        if (error) {
          console.error('Unable to list clouds', error);
          return;
        }
        const cloudList = [];
        if (clouds) {
          cloudList = Object.keys(clouds).map(id => {
            const cloud = clouds[id];
            cloud.id = id;
            return cloud;
          });
        }
        this.setState({
          clouds: cloudList,
          cloudsLoading: false
        });
        // If there is only one cloud then automatically select it.
        if (cloudList.length === 1) {
          this.props.setCloud(cloudList[0]);
        }
      });
    },

    /**
      Generate the list of clouds.

      @method _generateClouds
      @returns {Object} The cloud list.
    */
    _generateClouds: function() {
      if (this.state.cloudsLoading) {
        return (

            <juju.components.Spinner />

);
      }
      if (this.props.cloud) {
        return;
      }
      var clouds = [];
      this.state.clouds.forEach((cloud, i) => {
        var classes = classNames(
          'deployment-cloud__cloud',
          'four-col',
          {'last-col': i % 3 === 2});
        clouds.push(

              {this._generateLogo(cloud)}

);
      });
      return (

          {clouds}

);
    },

    /**
      Generate the logo for the selected cloud.

      @method _generateCloud
      @returns {Object} The cloud.
    */
    _generateCloud: function() {
      var cloud = this.props.cloud;
      if (!cloud) {
        return;
      }
      return (

          {this._generateLogo(cloud)}

);
    },

    /**
      Generate the logo for a cloud;

      @method _generateLogo
      @param {Object} cloud A cloud.
      @returns {Array} The logo.
    */
    _generateLogo: function(cloud) {
      var info = this.props.clouds[cloud.id];
      if (!info) {
        return cloud.name;
      }
      const id = info.id.replace('cloud-', '');
      return info.showLogo ? (
        <juju.components.SvgIcon
        height={info.svgHeight}
        name={id}
        width={info.svgWidth} />) : info.title;
    },

    render: function() {
      return (

          {this._generateClouds()}
          {this._generateCloud()}

      );
    }

  });

}, '0.1.0', {
  requires: [
    'loading-spinner',
    'svg-icon'
  ]
});
<|endoftext|>