<commit_before>/**
 * @file Guild events file.
 * @author Capuccino
 * @author Ovyerus
 */

module.exports = bot => {
    bot.on('guildCreate', async guild => {
        if (guild.members.filter(m => m.bot).filter / guild.members.size >= 0.50) {
            logger.info(`Detected bot collection guild '${guild.name}' (${guild.id}). Autoleaving...`);
            await guild.leave();
        } else {
        	if (!bot.config.url && bot.config.gameURL) {
        		await bot.editStatus('online', {
        			 name: `${bot.config.gameName || `${bot.config.mainPrefix}help for commands!`} | ${bot.guilds.size} ${bot.guilds.size === 1 ? 'server' : 'servers'}`,
        			 type : 0,
        			 url: null
        		});
        	} else {
        		await bot.editStatus('online', {
        			 name: `${bot.config.gameName || `${bot.config.mainPrefix}help for commands!`} | ${bot.guilds.size} ${bot.guilds.size === 1 ? 'server' : 'servers'}`,
        			 type : 1,
        			 url: bot.config.gameURL
        		});
        	}
            await bot.postGuildCount();
        }
    });

    bot.on('guildDelete', async guild => {
       if (!bot.config.url && bot.config.gameURL) {
        	await bot.editStatus('online', {
        	  name: `${bot.config.gameName || `${bot.config.mainPrefix}help for commands!`} | ${bot.guilds.size} ${bot.guilds.size === 1 ? 'server' : 'servers'}`,
        	  type : 0,
        	  url: null
        		});
        } else {
           await bot.editStatus('online', {
        		name: `${bot.config.gameName || `${bot.config.mainPrefix}help for commands!`} | ${bot.guilds.size} ${bot.guilds.size === 1 ? 'server' : 'servers'}`,
        		type : 1,
        		url: bot.config.gameURL
        	});
        }
         await bot.postGuildCount();
        };
    });

    bot.on('guildMemberAdd', async (guild, member) => {
        let res = await bot.getGuildSettings(guild.id);

        if (!res || !res.greeting || !res.greeting.enabled || !res.greeting.channelID || !res.greeting.message) return;

        let msg = res.greeting.message.replace(/{{user}}/g, member.mention).replace(/{{name}}/g, utils.formatUsername(member));

        return guild.channels.get(res.greeting.channelID).createMessage(msg);
    });

    bot.on('guildMemberDelete', async (guild, member) => {
        let res = await bot.guildSettings(guild.id);

        if (!res || !res.goodbye || !res.goodbye.enabled || !res.goodbye.channelID || !res.goodbye.message) return;

        let msg = res.goodbye.message.replace(/{{user}}/g, utils.formatUsername(member)).replace(/{{name}/gi, member.username);

        await guild.channels.get(res.goodbye.channelID).createMessage(msg);
    });
};
<commit_msg>fix syntax error<commit_after>/**
 * @file Guild events file.
 * @author Capuccino
 * @author Ovyerus
 */

module.exports = bot => {
    bot.on('guildCreate', async guild => {
        if (guild.members.filter(m => m.bot).filter / guild.members.size >= 0.50) {
            logger.info(`Detected bot collection guild '${guild.name}' (${guild.id}). Autoleaving...`);
            await guild.leave();
        } else {
        	if (!bot.config.url && bot.config.gameURL) {
        		await bot.editStatus('online', {
        			 name: `${bot.config.gameName || `${bot.config.mainPrefix}help for commands!`} | ${bot.guilds.size} ${bot.guilds.size === 1 ? 'server' : 'servers'}`,
        			 type : 0,
        			 url: null
        		});
        	} else {
        		await bot.editStatus('online', {
        			 name: `${bot.config.gameName || `${bot.config.mainPrefix}help for commands!`} | ${bot.guilds.size} ${bot.guilds.size === 1 ? 'server' : 'servers'}`,
        			 type : 1,
        			 url: bot.config.gameURL
        		});
        	}
            await bot.postGuildCount();
        }
    });

    bot.on('guildDelete', async guild => {
       if (!bot.config.url && bot.config.gameURL) {
        	await bot.editStatus('online', {
        	  name: `${bot.config.gameName || `${bot.config.mainPrefix}help for commands!`} | ${bot.guilds.size} ${bot.guilds.size === 1 ? 'server' : 'servers'}`,
        	  type : 0,
        	  url: null
        		});
        } else {
           await bot.editStatus('online', {
        		name: `${bot.config.gameName || `${bot.config.mainPrefix}help for commands!`} | ${bot.guilds.size} ${bot.guilds.size === 1 ? 'server' : 'servers'}`,
        		type : 1,
        		url: bot.config.gameURL
        	});
        }
         await bot.postGuildCount();
    });

    bot.on('guildMemberAdd', async (guild, member) => {
        let res = await bot.getGuildSettings(guild.id);

        if (!res || !res.greeting || !res.greeting.enabled || !res.greeting.channelID || !res.greeting.message) return;

        let msg = res.greeting.message.replace(/{{user}}/g, member.mention).replace(/{{name}}/g, utils.formatUsername(member));

        return guild.channels.get(res.greeting.channelID).createMessage(msg);
    });

    bot.on('guildMemberDelete', async (guild, member) => {
        let res = await bot.guildSettings(guild.id);

        if (!res || !res.goodbye || !res.goodbye.enabled || !res.goodbye.channelID || !res.goodbye.message) return;

        let msg = res.goodbye.message.replace(/{{user}}/g, utils.formatUsername(member)).replace(/{{name}/gi, member.username);

        await guild.channels.get(res.goodbye.channelID).createMessage(msg);
    });
};
<|endoftext|>


<commit_before>'kiwi public';

import xhr from 'xhr';
import _ from 'lodash';
import JSON5 from 'json5';
import Logger from './Logger';

let log = Logger.namespace('ConfigLoader');

export default class ConfigLoader {
    constructor() {
        this.config = Object.create(null);
        this.valReplacements = Object.create(null);
    }

    addValueReplacement(key, value) {
        this.valReplacements[key] = value;
        return this;
    }

    loadFromUrl(configUrl) {
        return new Promise((resolve, reject) => {
            xhr({ url: configUrl }, (err, response) => {
                if (err) {
                    reject();
                    return;
                }

                let configObj = null;
                try {
                    configObj = JSON5.parse(response.body);
                } catch (parseErr) {
                    log.error('Config ' + parseErr.message);
                    let errMsg = 'Config file error: ' + parseErr.message.replace('JSON5: ', '');
                    // Convert "at 22:16" to "at line 22, position 16"
                    /* eslint-disable arrow-body-style */
                    errMsg = errMsg.replace(/at (\d+):(\d+)/g, (m, m1, m2) => {
                        return `line ${m1}, position ${m2}`;
                    });
                    reject(errMsg);
                    return;
                }

                this.setConfig(configObj);
                resolve(this.config);
            });
        });
    }

    loadFromObj(configObj) {
        return new Promise((resolve, reject) => {
            this.setConfig(configObj);
            resolve(this.config);
        });
    }

    setConfig(confObj) {
        this.config = Object.create(null);
        _.each(confObj, (_val, key) => {
            let val = _val;
            if (typeof val === 'string') {
                val = this.insertReplacements(val);
            }

            this.config[key] = val;
        });
    }

    insertReplacements(input) {
        let out = input;
        Object.keys(this.valReplacements).forEach((k) => {
            out = out.replace('{{' + k + '}}', this.valReplacements[k]);
        });
        return out;
    }
}
<commit_msg>ConfigLoader deep object string replacements<commit_after>'kiwi public';

import xhr from 'xhr';
import _ from 'lodash';
import JSON5 from 'json5';
import Logger from './Logger';

let log = Logger.namespace('ConfigLoader');

export default class ConfigLoader {
    constructor() {
        this.config = Object.create(null);
        this.valReplacements = Object.create(null);
    }

    addValueReplacement(key, value) {
        this.valReplacements[key] = value;
        return this;
    }

    loadFromUrl(configUrl) {
        return new Promise((resolve, reject) => {
            xhr({ url: configUrl }, (err, response) => {
                if (err) {
                    reject();
                    return;
                }

                let configObj = null;
                try {
                    configObj = JSON5.parse(response.body);
                } catch (parseErr) {
                    log.error('Config ' + parseErr.message);
                    let errMsg = 'Config file error: ' + parseErr.message.replace('JSON5: ', '');
                    // Convert "at 22:16" to "at line 22, position 16"
                    /* eslint-disable arrow-body-style */
                    errMsg = errMsg.replace(/at (\d+):(\d+)/g, (m, m1, m2) => {
                        return `line ${m1}, position ${m2}`;
                    });
                    reject(errMsg);
                    return;
                }

                this.setConfig(configObj);
                resolve(this.config);
            });
        });
    }

    loadFromObj(configObj) {
        return new Promise((resolve, reject) => {
            this.setConfig(configObj);
            resolve(this.config);
        });
    }

    setConfig(confObj) {
        let walkObject = (obj, target) => {
            _.each(obj, (_val, key) => {
                let val = _val;
                if (typeof val === 'string') {
                    val = this.insertReplacements(val);
                    target[key] = val;
                } else if (typeof val === 'object') {
                    target[key] = Object.create(null);
                    walkObject(val, target[key]);
                }
            });
        };

        this.config = Object.create(null);
        walkObject(confObj, this.config);
    }

    insertReplacements(input) {
        let out = input;
        Object.keys(this.valReplacements).forEach((k) => {
            out = out.replace('{{' + k + '}}', this.valReplacements[k]);
        });
        return out;
    }
}
<|endoftext|>


<commit_before>/* @flow */
import React, { PureComponent } from 'react';
import { View, StyleSheet, Dimensions, Easing } from 'react-native';
import PhotoView from 'react-native-photo-view';
import { connectActionSheet } from '@expo/react-native-action-sheet';

import type { Actions, Auth, Message } from '../types';
import connectWithActions from '../connectWithActions';
import { getAuth } from '../selectors';
import { getResource } from '../utils/url';
import AnimatedLightboxHeader from './AnimatedLightboxHeader';
import AnimatedLightboxFooter from './AnimatedLightboxFooter';
import { constructActionSheetButtons, executeActionSheetAction } from './LightboxActionSheet';
import { NAVBAR_SIZE } from '../styles';
import { getGravatarFromEmail } from '../utils/avatar';

const styles = StyleSheet.create({
  img: {
    height: 300,
    flex: 1,
  },
  header: {},
  overlay: {
    backgroundColor: 'black',
    opacity: 0.8,
    position: 'absolute',
    zIndex: 1,
  },
  container: {
    flex: 1,
    justifyContent: 'space-between',
    alignItems: 'center',
  },
});

type Props = {
  auth: Auth,
  actions: Actions,
  src: string,
  message: Message,
  handleImagePress: (movement: string) => void,
  showActionSheetWithOptions: (Object, (number) => void) => void,
};

type State = {
  movement: 'in' | 'out',
};

class Lightbox extends PureComponent<Props, State> {
  props: Props;
  state: State;

  state = {
    movement: 'out',
  };

  handleImagePress = () => {
    this.setState(({ movement }, props) => ({
      movement: movement === 'out' ? 'in' : 'out',
    }));
  };

  handleOptionsPress = () => {
    const options = constructActionSheetButtons();
    const cancelButtonIndex = options.length - 1;
    const { showActionSheetWithOptions, src, auth } = this.props;
    showActionSheetWithOptions(
      {
        options,
        cancelButtonIndex,
      },
      buttonIndex => {
        executeActionSheetAction({
          title: options[buttonIndex],
          src,
          auth,
        });
      },
    );
  };

  handlePressBack = () => {
    const { actions } = this.props;
    actions.navigateBack();
  };

  getAnimationProps = () => ({
    easing: Easing.bezier(0.075, 0.82, 0.165, 1),
    duration: 300,
    movement: this.state.movement,
  });

  render() {
    const { src, message, auth } = this.props;
    const footerMessage =
      message.type === 'stream' ? `Shared in #${message.display_recipient}` : 'Shared with you';
    const resource = getResource(src, auth);
    const { width, height } = Dimensions.get('window');

    return (
      <View style={styles.container}>
        <AnimatedLightboxHeader
          onPressBack={this.handlePressBack}
          style={[styles.overlay, styles.header, { width }]}
          from={-NAVBAR_SIZE}
          to={0}
          timestamp={message.timestamp}
          avatarUrl={message.avatar_url || getGravatarFromEmail(message.sender_email)}
          senderName={message.sender_full_name}
          realm={auth.realm}
          {...this.getAnimationProps()}
        />
        <PhotoView
          source={resource}
          style={[styles.img, { width }]}
          resizeMode="contain"
          onTap={this.handleImagePress}
        />
        <AnimatedLightboxFooter
          style={[styles.overlay, { width, bottom: height - 44 }]}
          displayMessage={footerMessage}
          onOptionsPress={this.handleOptionsPress}
          from={height}
          to={height - 44}
          {...this.getAnimationProps()}
        />
      </View>
    );
  }
}

export default connectActionSheet(
  connectWithActions(state => ({
    auth: getAuth(state),
  }))(Lightbox),
);
<commit_msg>lightbox: Use standard Redux dispatch.<commit_after>/* @flow */
import { connect } from 'react-redux';

import React, { PureComponent } from 'react';
import { View, StyleSheet, Dimensions, Easing } from 'react-native';
import PhotoView from 'react-native-photo-view';
import { connectActionSheet } from '@expo/react-native-action-sheet';

import type { Auth, Dispatch, Message } from '../types';
import { getAuth } from '../selectors';
import { getResource } from '../utils/url';
import AnimatedLightboxHeader from './AnimatedLightboxHeader';
import AnimatedLightboxFooter from './AnimatedLightboxFooter';
import { constructActionSheetButtons, executeActionSheetAction } from './LightboxActionSheet';
import { NAVBAR_SIZE } from '../styles';
import { getGravatarFromEmail } from '../utils/avatar';
import { navigateBack } from '../actions';

const styles = StyleSheet.create({
  img: {
    height: 300,
    flex: 1,
  },
  header: {},
  overlay: {
    backgroundColor: 'black',
    opacity: 0.8,
    position: 'absolute',
    zIndex: 1,
  },
  container: {
    flex: 1,
    justifyContent: 'space-between',
    alignItems: 'center',
  },
});

type Props = {
  auth: Auth,
  dispatch: Dispatch,
  src: string,
  message: Message,
  handleImagePress: (movement: string) => void,
  showActionSheetWithOptions: (Object, (number) => void) => void,
};

type State = {
  movement: 'in' | 'out',
};

class Lightbox extends PureComponent<Props, State> {
  props: Props;
  state: State;

  state = {
    movement: 'out',
  };

  handleImagePress = () => {
    this.setState(({ movement }, props) => ({
      movement: movement === 'out' ? 'in' : 'out',
    }));
  };

  handleOptionsPress = () => {
    const options = constructActionSheetButtons();
    const cancelButtonIndex = options.length - 1;
    const { showActionSheetWithOptions, src, auth } = this.props;
    showActionSheetWithOptions(
      {
        options,
        cancelButtonIndex,
      },
      buttonIndex => {
        executeActionSheetAction({
          title: options[buttonIndex],
          src,
          auth,
        });
      },
    );
  };

  handlePressBack = () => {
    const { dispatch } = this.props;
    dispatch(navigateBack());
  };

  getAnimationProps = () => ({
    easing: Easing.bezier(0.075, 0.82, 0.165, 1),
    duration: 300,
    movement: this.state.movement,
  });

  render() {
    const { src, message, auth } = this.props;
    const footerMessage =
      message.type === 'stream' ? `Shared in #${message.display_recipient}` : 'Shared with you';
    const resource = getResource(src, auth);
    const { width, height } = Dimensions.get('window');

    return (
      <View style={styles.container}>
        <AnimatedLightboxHeader
          onPressBack={this.handlePressBack}
          style={[styles.overlay, styles.header, { width }]}
          from={-NAVBAR_SIZE}
          to={0}
          timestamp={message.timestamp}
          avatarUrl={message.avatar_url || getGravatarFromEmail(message.sender_email)}
          senderName={message.sender_full_name}
          realm={auth.realm}
          {...this.getAnimationProps()}
        />
        <PhotoView
          source={resource}
          style={[styles.img, { width }]}
          resizeMode="contain"
          onTap={this.handleImagePress}
        />
        <AnimatedLightboxFooter
          style={[styles.overlay, { width, bottom: height - 44 }]}
          displayMessage={footerMessage}
          onOptionsPress={this.handleOptionsPress}
          from={height}
          to={height - 44}
          {...this.getAnimationProps()}
        />
      </View>
    );
  }
}

export default connectActionSheet(
  connect(state => ({
    auth: getAuth(state),
  }))(Lightbox),
);
<|endoftext|>


<commit_before>/*
 * An classic model layer for todo list application
 * The Store is dummy, and really backed in memory, but simulates asynch access to backend
 */
var todo = (function (ns, undefined) {
  ns = ns || {};
  ns.model = ns.model || {};

  ns.model.Tasks = function (store, optTaskFactory) {
    var makeTask = optTaskFactory || function (dto) {
      return new ns.model.Task(dto, store);
    };
    var allTasks = function (callback) {
      store.all(function (dtos) {
        callback(dtos.map(makeTask));
      });
    };

    this.newTask = function (description, callback) {
      var task = makeTask({
        description:description,
        done:false
      });
      task.save(function (task) {
        callback(task);
      });
      return task;
    };
    this.forEach = function (callback) {
      allTasks(function (tasks) {
        tasks.forEach(callback);
      });
    };
  };

  ns.model.Task = function (initialData, store) {
    var id = initialData.id;
    var description = initialData.description;
    var done = initialData.done;
    this.toDTO = function () {
      return {
        'description':description,
        'id':id,
        'done':done
      };
    };
    this.done = function (optIsDone) {
      if(typeof optIsDone === 'boolean' && optIsDone !== done)
        done = !done;
      return done;
    };
    this.save = function (callback) {
      var self = this;
      store.save(self.toDTO(), function (dto) {
        id = dto.id;
        callback(self);
      });
    };
  };

  return ns;
}(todo));<commit_msg>Implemented. But what if there is no tasks?<commit_after>/*
 * An classic model layer for todo list application
 * The Store is dummy, and really backed in memory, but simulates asynch access to backend
 */
var todo = (function (ns, undefined) {
  ns = ns || {};
  ns.model = ns.model || {};

  ns.model.Tasks = function (store, optTaskFactory) {
    var makeTask = optTaskFactory || function (dto) {
      return new ns.model.Task(dto, store);
    };
    var allTasks = function (callback) {
      store.all(function (dtos) {
        callback(dtos.map(makeTask));
      });
    };

    this.newTask = function (description, callback) {
      var task = makeTask({
        description:description,
        done:false
      });
      task.save(function (task) {
        callback(task);
      });
      return task;
    };
    this.forEach = function (callback) {
      allTasks(function (tasks) {
        var numberOfTasks = tasks.length;
        for (var i = 0; i < numberOfTasks; i++)
          callback(tasks[i], i === (numberOfTasks - 1));
      });
    };
  };

  ns.model.Task = function (initialData, store) {
    var id = initialData.id;
    var description = initialData.description;
    var done = initialData.done;
    this.toDTO = function () {
      return {
        'description':description,
        'id':id,
        'done':done
      };
    };
    this.done = function (optIsDone) {
      if (typeof optIsDone === 'boolean' && optIsDone !== done)
        done = !done;
      return done;
    };
    this.save = function (callback) {
      var self = this;
      store.save(self.toDTO(), function (dto) {
        id = dto.id;
        callback(self);
      });
    };
  };

  return ns;
}(todo));<|endoftext|>


<commit_before>/*global define, Intl*/
define([
	"dojo/_base/declare",
	"dojo/Evented",
	"dojo/on",
	"esri/config",
	"esri/request",
	"esri/graphic",
	"esri/geometry/jsonUtils",
	"esri/layers/FeatureLayer",
	"esri/InfoTemplate",
	"esri/symbols/SimpleMarkerSymbol",
	"esri/symbols/SimpleLineSymbol",
	"esri/Color",
	"esri/renderers/UniqueValueRenderer",
	"elc/elc-ui/ElcUI",
	"elc"
], function (declare, Evented, on, esriConfig, esriRequest, Graphic, geometryJsonUtils, FeatureLayer, InfoTemplate, SimpleMarkerSymbol, SimpleLineSymbol, Color, UniqueValueRenderer, ElcUI, Elc) {
	var elcUI, routeLocator, pointResultsLayer, lineResultsLayer;

	var isWsdotInternal;

	esriConfig.defaults.io.corsEnabledServers.push("wwwi.wsdot.wa.gov");

	// Test to see if the client is within the WSDOT internal network.
	esriRequest({
		url: "http://wwwi.wsdot.wa.gov/geosvcs/ArcGIS/rest/services?f=json"
	}).then(function () {
		isWsdotInternal = true;
	}, function () {
		isWsdotInternal = false;
	});

	var wsdotLogoGreen = new Color([0, 123, 95]); // This is the same color as the WSDOT logo.
	var eventColor = new Color([255, 100, 100]); // Color used to indicate where user has clicked as opposed to actual route location.

	var routePointOutlineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255,255,255,0.8]), 2);
	var routePointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 12, routePointOutlineSymbol, wsdotLogoGreen);
	var eventPointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 12, eventLineSymbol, eventColor);
	var routeLineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, wsdotLogoGreen, 3);
	var eventLineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SHORTDOT, eventColor, 3);

	var pointRenderer = new UniqueValueRenderer(routePointSymbol, "IsEvent");
	var lineRenderer = new UniqueValueRenderer(routeLineSymbol, "IsEvent");

	pointRenderer.addValue({
		description: "Where the user clicked",
		label: "Event",
		symbol: eventPointSymbol,
		value: 1
	});

	pointRenderer.addValue({
		description: "Route location",
		label: "Route Location",
		symbol: routePointSymbol,
		value: 0
	});

	lineRenderer.addValue({
		description: "Where the user clicked",
		label: "Event",
		symbol: eventLineSymbol,
		value: 1
	});

	lineRenderer.addValue({
		description: "Route location",
		label: "Route Location",
		symbol: routeLineSymbol,
		value: 0
	});

	/**
	 * A set of graphics for a route location.
	 */
	function RouteLocationGraphicSet(/**{elc/RouteLocation}*/ routeLocation) {
		var geometry, attributes, graphic;
		/** @member {?esri/Graphic} Actual point or line segment on a route */
		this.routeFeature = null;
		/** @member {?esri/Graphic} In a find nearest operation, where the user clicked. */
		this.eventPointFeature = null;
		/** @member {?esri/Graphic} A line that connects the routeFeature to the eventPointFeature. */
		this.connectorLineFeature = null;

		var spatialReference = { wkid: 3857 };

		if (routeLocation) {
		    if (routeLocation.RouteGeometry && !routeLocation.RouteGeometry.points) {
				if (!routeLocation.RouteGeometry.spatialReference) {
					routeLocation.RouteGeometry.spatialReference = spatialReference;
				}
				geometry = geometryJsonUtils.fromJson(routeLocation.RouteGeometry);
				attributes = routeLocation.toJSON();
				attributes.IsEvent = 0;
				delete attributes.RouteGeometry;
				delete attributes.EventPoint;
				graphic = new Graphic(geometry, null, attributes);
				this.routeFeature = graphic;
			}
			if (routeLocation.EventPoint) {
				if (!routeLocation.EventPoint.spatialReference) {
					routeLocation.EventPoint.spatialReference = spatialReference;
				}
				geometry = geometryJsonUtils.fromJson(routeLocation.EventPoint);
				attributes = routeLocation.toJSON();
				attributes.IsEvent = 1;
				delete attributes.RouteGeometry;
				delete attributes.EventPoint;
				graphic = new Graphic(geometry, null, attributes);
				this.eventPointFeature = graphic;
			}
			if (routeLocation.RouteGeometry && routeLocation.EventPoint) {
				geometry = geometryJsonUtils.fromJson({
					paths: [
						[
							[this.routeFeature.geometry.x, this.routeFeature.geometry.y],
							[this.eventPointFeature.geometry.x, this.eventPointFeature.geometry.y]
						]
					],
					spatialReference: spatialReference
				});
				attributes = routeLocation.toJSON();
				attributes.IsEvent = 1;
				delete attributes.RouteGeometry;
				delete attributes.EventPoint;
				graphic = new Graphic(geometry, null, attributes);
				this.connectorLineFeature = graphic;
			}
		}
	}

	/**
	 * Creates popup content from graphic attributes.
	 * @param {esri/Graphic} graphic
	 * @returns {HTMLDListElement}
	 */
	function graphicToHtml(graphic) {
		var frag, dl, dt, dd, v, codeField, ignoredFields, layer, distanceFieldRe, nFmt;

		frag = document.createDocumentFragment();

		dl = document.createElement("dl");
		frag.appendChild(dl);
		codeField = /Code$/i;
		ignoredFields = /^IsEvent$/i;
		layer = graphic._layer;
		distanceFieldRe = /^Distance$/i;
		nFmt;
		// Not all browsers support Internationalization API. Those browsers will just get an unformatted number.
		if (window.Intl) {
			nFmt = new Intl.NumberFormat();
		}

		layer.fields.forEach(function (field) {
			if (graphic.attributes.hasOwnProperty(field.name) && !ignoredFields.test(field.name)) {
				v = graphic.attributes[field.name];
				if (v || (v === 0 && !codeField.test(field.name))) {
					dt = document.createElement("dt");
					dt.textContent = field.alias || field.name;
					dd = document.createElement("dd");
					if (distanceFieldRe.test(field.name) && nFmt) {
						dd.textContent = [nFmt.format(v), "ft."].join(" ");
					} else {
						dd.textContent = v;
					}
					dl.appendChild(dt);
					dl.appendChild(dd);
				}
			}
		});

		var p,a;
		// Add SRView URL if user is inside WSDOT Intranet.
		if (isWsdotInternal) {
			p = document.createElement("p");
			a = document.createElement("a");
			var routeId = new Elc.RouteId(graphic.attributes.Route);
			var url = "http://srview3i.wsdot.loc/stateroute/picturelog/v3/client/SRview.Windows.Viewer.application";
			var params = {
				srnum: routeId.sr,
				rrt: routeId.rrt ? ["rrt", routeId.rrt] : null,
				rrq: routeId.rrq ? ["rrq", routeId.rrq] : null,
				dir: graphic.attributes.Decrease ? "D" : null,
				srmp: graphic.attributes.Srmp,
				back: graphic.attributes.Back
			};

			var query = [];
			for (var k in params) {
				if (params.hasOwnProperty(k) && params[k] !== null) {
					query.push([k, params[k]].join("="));
				}
			}
			query = query.join("&");
			url = [url, query].join("?");

			a.href = url;
			a.target = "_blank";
			a.textContent = "SRView";
			a.setAttribute("class", "srview clickonce");

			p.appendChild(a);

			frag.appendChild(p);
		}

		return frag;
	}

	var ArcGisElcUI = declare(Evented, {
		constructor: function (domNode, options) {

			var self = this;

			/**
			 * Adds ELC results to feature layers.
			 */
			function addResultsToMap(elcResults) {
				var nonGraphics, graphics;
				if (elcResults && Array.isArray(elcResults)) {
					if (elcResults.length === 0) {
						self.emit("elc-results-not-found", { elcResults: elcResults });
					} else {
						pointResultsLayer.suspend();
						lineResultsLayer.suspend();
						nonGraphics = [];
						graphics = [];
						elcResults.forEach(function (routeLocation) {
							var gSet = new RouteLocationGraphicSet(routeLocation);
							if (gSet.routeFeature) {
								if (gSet.routeFeature.geometry.type === "point") {
									pointResultsLayer.add(gSet.routeFeature);
								} else if (gSet.routeFeature.geometry.type === "polyline") {
									lineResultsLayer.add(gSet.routeFeature);
								} else {
									console.warn("Unexpected geometry type", gSet.routeFeature);
								}
								graphics.push(gSet.routeFeature);
							} else {
								nonGraphics.push(routeLocation);
							}
							if (gSet.eventPointFeature) {
								pointResultsLayer.add(gSet.eventPointFeature);
							}
							if (gSet.connectorLineFeature) {
								lineResultsLayer.add(gSet.connectorLineFeature);
							}

						});
						pointResultsLayer.resume();
						lineResultsLayer.resume();
						self.emit("elc-results-found", {
							elcResults: elcResults,
							graphics: graphics
						});
					}
				}
				if (nonGraphics && nonGraphics.length > 0) {
					self.emit("non-geometry-results-returned", {
						elcResults: elcResults,
						nonGeometryResults: nonGraphics
					});
				}
			}

			routeLocator = new Elc.RouteLocator();

			elcUI = new ElcUI(domNode, options);

			elcUI.root.addEventListener('find-route-location-submit', function (e) {
				var locations = [new Elc.RouteLocation(e.detail)];
				routeLocator.findRouteLocations({
					locations: locations,
					outSR: 3857
				}).then(addResultsToMap, function (error) {
					console.error("find route location error", error);
				});
			});

			elcUI.root.addEventListener('find-nearest-route-location-submit', function (e) {
				// Setup map click event. This should only occur ONCE.
				on.once(self.map, "click", function (mapEvt) {
					var mapPoint = mapEvt.mapPoint;
					routeLocator.findNearestRouteLocations({
						coordinates: [mapPoint.x, mapPoint.y],
						searchRadius: e.detail.radius,
						inSR: mapPoint.spatialReference.wkid,
						outSR: mapPoint.spatialReference.wkid,
						referenceDate: new Date()
					}).then(function (elcResults) {
						if (elcResults.length) {
							elcResults = elcResults.map(function (r) {
								r = r.toJSON();
								delete r.Coordinates;
								delete r.Angle;
								delete r.RouteGeometry;
								delete r.RealignmentDate;
								delete r.ArmCalcReturnCode;
								delete r.ArmCalcReturnMessage;
								//delete r.EventPoint;
								return new Elc.RouteLocation(r);
							});
							routeLocator.findRouteLocations({
								locations: elcResults,
								outSR: 3857
							}).then(addResultsToMap, function (error) {
								console.error("find route location error", error);
							});
						} else {
							addResultsToMap(elcResults).then(addResultsToMap);
						}
					}, function (error) {
						console.error("find nearest route location error", error);
					});
				});
			});

			var infoTemplate = new InfoTemplate("Route Location", graphicToHtml);

			var elcFields = [
					{ name: "Id", type: "esriFieldTypeInteger", alias: "ID" },
					{ name: "Route", type: "esriFieldTypeString" },
					{ name: "Decrease", type: "esriFieldTypeSmallInteger" },
					{ name: "Arm", type: "esriFieldTypeDouble", alias: "ARM" },
					{ name: "Srmp", type: "esriFieldTypeDouble", alias: "SRMP" },
					{ name: "Back", type: "esriFieldTypeSmallInteger" },
					{ name: "ReferenceDate", type: "esriFieldTypeDate", alias: "Reference Date" },
					{ name: "ResponseDate", type: "esriFieldTypeDate", alias: "Response Date" },
					{ name: "RealignmentDate", type: "esriFieldTypeDate", alias: "Realignment Date" },
					{ name: "EndArm", type: "esriFieldTypeDouble", alias: "End ARM" },
					{ name: "EndSrmp", type: "esriFieldTypeDouble", alias: "End SRMP" },
					{ name: "EndBack", type: "esriFieldTypeSmallInteger", alias: "End Back" },
					{ name: "EndReferenceDate", type: "esriFieldTypeDate", alias: "End Reference Date" },
					{ name: "EndResponseDate", type: "esriFieldTypeDate", alias: "End Response Date" },
					{ name: "EndRealignDate", type: "esriFieldTypeDate", alias: "End Realignment Date" },
					{ name: "ArmCalcReturnCode", type: "esriFieldTypeInteger", alias: "ArmCalc Return Code" },
					{ name: "ArmCalcEndReturnCode", type: "esriFieldTypeInteger", alias: "ArmCalc End Return Code" },
					{ name: "ArmCalcReturnMessage", type: "esriFieldTypeString", alias: "ArmCalc Return Message" },
					{ name: "ArmCalcEndReturnMessage", type: "esriFieldTypeString", alias: "ArmCalc End Return Message" },
					{ name: "LocatingError", type: "esriFieldTypeString", alias: "Locating Error" },
					//{ name: "RouteGeometry", type: "esriFieldTypeGeometry" },
					//{ name: "EventPoint", type: "esriFieldTypeGeometry" },
					{ name: "Distance", alias: "Offset", type: "esriFieldTypeDouble" },
					{ name: "Angle", type: "esriFieldTypeDouble" },
					{ name: "IsEvent", type: "esriFieldTypeSmallInteger", alias: "Is Event"}
			];

			lineResultsLayer = new FeatureLayer({
				featureSet: null,
				layerDefinition: {
					geometryType: "esriGeometryPolyline",
					fields: elcFields
				}
			}, {
				id: "Located Segment",
				infoTemplate: infoTemplate
			});
			lineResultsLayer.setRenderer(lineRenderer);

			pointResultsLayer = new FeatureLayer({
				featureSet: null,
				layerDefinition: {
					geometryType: "esriGeometryPoint",
					fields: elcFields
				}
			}, {
				id: "Located Milepost",
				infoTemplate: infoTemplate
			});

			pointResultsLayer.setRenderer(pointRenderer);

			this._layers = {
				pointResults: pointResultsLayer,
				lineResults: lineResultsLayer
			};

			var clearBtn = document.querySelector(".clear-graphics-button");
			clearBtn.addEventListener("click", function () {
				pointResultsLayer.clear();
				lineResultsLayer.clear();
			});
		},
		setMap: function (map) {
			map.addLayer(this._layers.lineResults);
			map.addLayer(this._layers.pointResults);
			this.map = map;
		}
	});

	return ArcGisElcUI;
});<commit_msg>Replaced spaces with tab for consistency<commit_after>/*global define, Intl*/
define([
	"dojo/_base/declare",
	"dojo/Evented",
	"dojo/on",
	"esri/config",
	"esri/request",
	"esri/graphic",
	"esri/geometry/jsonUtils",
	"esri/layers/FeatureLayer",
	"esri/InfoTemplate",
	"esri/symbols/SimpleMarkerSymbol",
	"esri/symbols/SimpleLineSymbol",
	"esri/Color",
	"esri/renderers/UniqueValueRenderer",
	"elc/elc-ui/ElcUI",
	"elc"
], function (declare, Evented, on, esriConfig, esriRequest, Graphic, geometryJsonUtils, FeatureLayer, InfoTemplate, SimpleMarkerSymbol, SimpleLineSymbol, Color, UniqueValueRenderer, ElcUI, Elc) {
	var elcUI, routeLocator, pointResultsLayer, lineResultsLayer;

	var isWsdotInternal;

	esriConfig.defaults.io.corsEnabledServers.push("wwwi.wsdot.wa.gov");

	// Test to see if the client is within the WSDOT internal network.
	esriRequest({
		url: "http://wwwi.wsdot.wa.gov/geosvcs/ArcGIS/rest/services?f=json"
	}).then(function () {
		isWsdotInternal = true;
	}, function () {
		isWsdotInternal = false;
	});

	var wsdotLogoGreen = new Color([0, 123, 95]); // This is the same color as the WSDOT logo.
	var eventColor = new Color([255, 100, 100]); // Color used to indicate where user has clicked as opposed to actual route location.

	var routePointOutlineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255,255,255,0.8]), 2);
	var routePointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 12, routePointOutlineSymbol, wsdotLogoGreen);
	var eventPointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 12, eventLineSymbol, eventColor);
	var routeLineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, wsdotLogoGreen, 3);
	var eventLineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SHORTDOT, eventColor, 3);

	var pointRenderer = new UniqueValueRenderer(routePointSymbol, "IsEvent");
	var lineRenderer = new UniqueValueRenderer(routeLineSymbol, "IsEvent");

	pointRenderer.addValue({
		description: "Where the user clicked",
		label: "Event",
		symbol: eventPointSymbol,
		value: 1
	});

	pointRenderer.addValue({
		description: "Route location",
		label: "Route Location",
		symbol: routePointSymbol,
		value: 0
	});

	lineRenderer.addValue({
		description: "Where the user clicked",
		label: "Event",
		symbol: eventLineSymbol,
		value: 1
	});

	lineRenderer.addValue({
		description: "Route location",
		label: "Route Location",
		symbol: routeLineSymbol,
		value: 0
	});

	/**
	 * A set of graphics for a route location.
	 */
	function RouteLocationGraphicSet(/**{elc/RouteLocation}*/ routeLocation) {
		var geometry, attributes, graphic;
		/** @member {?esri/Graphic} Actual point or line segment on a route */
		this.routeFeature = null;
		/** @member {?esri/Graphic} In a find nearest operation, where the user clicked. */
		this.eventPointFeature = null;
		/** @member {?esri/Graphic} A line that connects the routeFeature to the eventPointFeature. */
		this.connectorLineFeature = null;

		var spatialReference = { wkid: 3857 };

		if (routeLocation) {
			if (routeLocation.RouteGeometry && !routeLocation.RouteGeometry.points) {
				if (!routeLocation.RouteGeometry.spatialReference) {
					routeLocation.RouteGeometry.spatialReference = spatialReference;
				}
				geometry = geometryJsonUtils.fromJson(routeLocation.RouteGeometry);
				attributes = routeLocation.toJSON();
				attributes.IsEvent = 0;
				delete attributes.RouteGeometry;
				delete attributes.EventPoint;
				graphic = new Graphic(geometry, null, attributes);
				this.routeFeature = graphic;
			}
			if (routeLocation.EventPoint) {
				if (!routeLocation.EventPoint.spatialReference) {
					routeLocation.EventPoint.spatialReference = spatialReference;
				}
				geometry = geometryJsonUtils.fromJson(routeLocation.EventPoint);
				attributes = routeLocation.toJSON();
				attributes.IsEvent = 1;
				delete attributes.RouteGeometry;
				delete attributes.EventPoint;
				graphic = new Graphic(geometry, null, attributes);
				this.eventPointFeature = graphic;
			}
			if (routeLocation.RouteGeometry && routeLocation.EventPoint) {
				geometry = geometryJsonUtils.fromJson({
					paths: [
						[
							[this.routeFeature.geometry.x, this.routeFeature.geometry.y],
							[this.eventPointFeature.geometry.x, this.eventPointFeature.geometry.y]
						]
					],
					spatialReference: spatialReference
				});
				attributes = routeLocation.toJSON();
				attributes.IsEvent = 1;
				delete attributes.RouteGeometry;
				delete attributes.EventPoint;
				graphic = new Graphic(geometry, null, attributes);
				this.connectorLineFeature = graphic;
			}
		}
	}

	/**
	 * Creates popup content from graphic attributes.
	 * @param {esri/Graphic} graphic
	 * @returns {HTMLDListElement}
	 */
	function graphicToHtml(graphic) {
		var frag, dl, dt, dd, v, codeField, ignoredFields, layer, distanceFieldRe, nFmt;

		frag = document.createDocumentFragment();

		dl = document.createElement("dl");
		frag.appendChild(dl);
		codeField = /Code$/i;
		ignoredFields = /^IsEvent$/i;
		layer = graphic._layer;
		distanceFieldRe = /^Distance$/i;
		nFmt;
		// Not all browsers support Internationalization API. Those browsers will just get an unformatted number.
		if (window.Intl) {
			nFmt = new Intl.NumberFormat();
		}

		layer.fields.forEach(function (field) {
			if (graphic.attributes.hasOwnProperty(field.name) && !ignoredFields.test(field.name)) {
				v = graphic.attributes[field.name];
				if (v || (v === 0 && !codeField.test(field.name))) {
					dt = document.createElement("dt");
					dt.textContent = field.alias || field.name;
					dd = document.createElement("dd");
					if (distanceFieldRe.test(field.name) && nFmt) {
						dd.textContent = [nFmt.format(v), "ft."].join(" ");
					} else {
						dd.textContent = v;
					}
					dl.appendChild(dt);
					dl.appendChild(dd);
				}
			}
		});

		var p,a;
		// Add SRView URL if user is inside WSDOT Intranet.
		if (isWsdotInternal) {
			p = document.createElement("p");
			a = document.createElement("a");
			var routeId = new Elc.RouteId(graphic.attributes.Route);
			var url = "http://srview3i.wsdot.loc/stateroute/picturelog/v3/client/SRview.Windows.Viewer.application";
			var params = {
				srnum: routeId.sr,
				rrt: routeId.rrt ? ["rrt", routeId.rrt] : null,
				rrq: routeId.rrq ? ["rrq", routeId.rrq] : null,
				dir: graphic.attributes.Decrease ? "D" : null,
				srmp: graphic.attributes.Srmp,
				back: graphic.attributes.Back
			};

			var query = [];
			for (var k in params) {
				if (params.hasOwnProperty(k) && params[k] !== null) {
					query.push([k, params[k]].join("="));
				}
			}
			query = query.join("&");
			url = [url, query].join("?");

			a.href = url;
			a.target = "_blank";
			a.textContent = "SRView";
			a.setAttribute("class", "srview clickonce");

			p.appendChild(a);

			frag.appendChild(p);
		}

		return frag;
	}

	var ArcGisElcUI = declare(Evented, {
		constructor: function (domNode, options) {

			var self = this;

			/**
			 * Adds ELC results to feature layers.
			 */
			function addResultsToMap(elcResults) {
				var nonGraphics, graphics;
				if (elcResults && Array.isArray(elcResults)) {
					if (elcResults.length === 0) {
						self.emit("elc-results-not-found", { elcResults: elcResults });
					} else {
						pointResultsLayer.suspend();
						lineResultsLayer.suspend();
						nonGraphics = [];
						graphics = [];
						elcResults.forEach(function (routeLocation) {
							var gSet = new RouteLocationGraphicSet(routeLocation);
							if (gSet.routeFeature) {
								if (gSet.routeFeature.geometry.type === "point") {
									pointResultsLayer.add(gSet.routeFeature);
								} else if (gSet.routeFeature.geometry.type === "polyline") {
									lineResultsLayer.add(gSet.routeFeature);
								} else {
									console.warn("Unexpected geometry type", gSet.routeFeature);
								}
								graphics.push(gSet.routeFeature);
							} else {
								nonGraphics.push(routeLocation);
							}
							if (gSet.eventPointFeature) {
								pointResultsLayer.add(gSet.eventPointFeature);
							}
							if (gSet.connectorLineFeature) {
								lineResultsLayer.add(gSet.connectorLineFeature);
							}

						});
						pointResultsLayer.resume();
						lineResultsLayer.resume();
						self.emit("elc-results-found", {
							elcResults: elcResults,
							graphics: graphics
						});
					}
				}
				if (nonGraphics && nonGraphics.length > 0) {
					self.emit("non-geometry-results-returned", {
						elcResults: elcResults,
						nonGeometryResults: nonGraphics
					});
				}
			}

			routeLocator = new Elc.RouteLocator();

			elcUI = new ElcUI(domNode, options);

			elcUI.root.addEventListener('find-route-location-submit', function (e) {
				var locations = [new Elc.RouteLocation(e.detail)];
				routeLocator.findRouteLocations({
					locations: locations,
					outSR: 3857
				}).then(addResultsToMap, function (error) {
					console.error("find route location error", error);
				});
			});

			elcUI.root.addEventListener('find-nearest-route-location-submit', function (e) {
				// Setup map click event. This should only occur ONCE.
				on.once(self.map, "click", function (mapEvt) {
					var mapPoint = mapEvt.mapPoint;
					routeLocator.findNearestRouteLocations({
						coordinates: [mapPoint.x, mapPoint.y],
						searchRadius: e.detail.radius,
						inSR: mapPoint.spatialReference.wkid,
						outSR: mapPoint.spatialReference.wkid,
						referenceDate: new Date()
					}).then(function (elcResults) {
						if (elcResults.length) {
							elcResults = elcResults.map(function (r) {
								r = r.toJSON();
								delete r.Coordinates;
								delete r.Angle;
								delete r.RouteGeometry;
								delete r.RealignmentDate;
								delete r.ArmCalcReturnCode;
								delete r.ArmCalcReturnMessage;
								//delete r.EventPoint;
								return new Elc.RouteLocation(r);
							});
							routeLocator.findRouteLocations({
								locations: elcResults,
								outSR: 3857
							}).then(addResultsToMap, function (error) {
								console.error("find route location error", error);
							});
						} else {
							addResultsToMap(elcResults).then(addResultsToMap);
						}
					}, function (error) {
						console.error("find nearest route location error", error);
					});
				});
			});

			var infoTemplate = new InfoTemplate("Route Location", graphicToHtml);

			var elcFields = [
					{ name: "Id", type: "esriFieldTypeInteger", alias: "ID" },
					{ name: "Route", type: "esriFieldTypeString" },
					{ name: "Decrease", type: "esriFieldTypeSmallInteger" },
					{ name: "Arm", type: "esriFieldTypeDouble", alias: "ARM" },
					{ name: "Srmp", type: "esriFieldTypeDouble", alias: "SRMP" },
					{ name: "Back", type: "esriFieldTypeSmallInteger" },
					{ name: "ReferenceDate", type: "esriFieldTypeDate", alias: "Reference Date" },
					{ name: "ResponseDate", type: "esriFieldTypeDate", alias: "Response Date" },
					{ name: "RealignmentDate", type: "esriFieldTypeDate", alias: "Realignment Date" },
					{ name: "EndArm", type: "esriFieldTypeDouble", alias: "End ARM" },
					{ name: "EndSrmp", type: "esriFieldTypeDouble", alias: "End SRMP" },
					{ name: "EndBack", type: "esriFieldTypeSmallInteger", alias: "End Back" },
					{ name: "EndReferenceDate", type: "esriFieldTypeDate", alias: "End Reference Date" },
					{ name: "EndResponseDate", type: "esriFieldTypeDate", alias: "End Response Date" },
					{ name: "EndRealignDate", type: "esriFieldTypeDate", alias: "End Realignment Date" },
					{ name: "ArmCalcReturnCode", type: "esriFieldTypeInteger", alias: "ArmCalc Return Code" },
					{ name: "ArmCalcEndReturnCode", type: "esriFieldTypeInteger", alias: "ArmCalc End Return Code" },
					{ name: "ArmCalcReturnMessage", type: "esriFieldTypeString", alias: "ArmCalc Return Message" },
					{ name: "ArmCalcEndReturnMessage", type: "esriFieldTypeString", alias: "ArmCalc End Return Message" },
					{ name: "LocatingError", type: "esriFieldTypeString", alias: "Locating Error" },
					//{ name: "RouteGeometry", type: "esriFieldTypeGeometry" },
					//{ name: "EventPoint", type: "esriFieldTypeGeometry" },
					{ name: "Distance", alias: "Offset", type: "esriFieldTypeDouble" },
					{ name: "Angle", type: "esriFieldTypeDouble" },
					{ name: "IsEvent", type: "esriFieldTypeSmallInteger", alias: "Is Event"}
			];

			lineResultsLayer = new FeatureLayer({
				featureSet: null,
				layerDefinition: {
					geometryType: "esriGeometryPolyline",
					fields: elcFields
				}
			}, {
				id: "Located Segment",
				infoTemplate: infoTemplate
			});
			lineResultsLayer.setRenderer(lineRenderer);

			pointResultsLayer = new FeatureLayer({
				featureSet: null,
				layerDefinition: {
					geometryType: "esriGeometryPoint",
					fields: elcFields
				}
			}, {
				id: "Located Milepost",
				infoTemplate: infoTemplate
			});

			pointResultsLayer.setRenderer(pointRenderer);

			this._layers = {
				pointResults: pointResultsLayer,
				lineResults: lineResultsLayer
			};

			var clearBtn = document.querySelector(".clear-graphics-button");
			clearBtn.addEventListener("click", function () {
				pointResultsLayer.clear();
				lineResultsLayer.clear();
			});
		},
		setMap: function (map) {
			map.addLayer(this._layers.lineResults);
			map.addLayer(this._layers.pointResults);
			this.map = map;
		}
	});

	return ArcGisElcUI;
});<|endoftext|>


<commit_before>/*
 * Copyright 2012 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Definitions for node's cluster module. Depends on the events module.
 * @see http://nodejs.org/api/cluster.html
 * @see https://github.com/joyent/node/blob/master/lib/cluster.js
 * @externs
 * @author Daniel Wirtz <dcode@dcode.io>
 */

/**
 BEGIN_NODE_INCLUDE
 var cluster = require('cluster');
 END_NODE_INCLUDE
 */

/**
 * @extends events.EventEmitter
 */
var cluster = function() {};

/**
 * @typedef {{exec: string, args: Array.<string>, silent: boolean}}
 */
cluster.Settings;

/**
 * @type {cluster.Settings}
 */
cluster.settings;

/**
 * @type {boolean}
 */
cluster.isMaster;

/**
 * @type {boolean}
 */
cluster.isWorker;

/**
 * @param {cluster.Settings=} settings
 */
cluster.setupMaster = function(settings) {};

/**
 * @param {Object.<string,*>} env
 * @return {cluster.Worker}
 */
cluster.fork = function(env) {};

/**
 * @param {function()=} callback
 */
cluster.disconnect = function(callback) {};

/**
 * @type {?cluster.Worker}
 */
cluster.worker;

/**
 * @type {?Object.<string,cluster.Worker>}
 */
cluster.workers;

/**
 * @constructor
 * @extends events.EventEmitter
 */
cluster.Worker = function() {};

/**
 * @type {string}
 */
cluster.Worker.prototype.id;

/**
 * @type {child_process.ChildProcess}
 */
cluster.Worker.prototype.process;

/**
 * @type {boolean}
 */
cluster.Worker.prototype.suicide;

/**
 * @param {Object} message
 * @param {*=} sendHandle
 */
cluster.Worker.prototype.send = function(message, sendHandle) {};

/**
 */
cluster.Worker.prototype.destroy = function() {};

/**
 */
cluster.Worker.prototype.disconnect = function() {};
<commit_msg>Fix type of cluster<commit_after>/*
 * Copyright 2012 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Definitions for node's cluster module. Depends on the events module.
 * @see http://nodejs.org/api/cluster.html
 * @see https://github.com/joyent/node/blob/master/lib/cluster.js
 * @externs
 * @author Daniel Wirtz <dcode@dcode.io>
 */

/**
 BEGIN_NODE_INCLUDE
 var cluster = require('cluster');
 END_NODE_INCLUDE
 */

/**
 * @type events.EventEmitter
 */
var cluster;

/**
 * @typedef {{exec: string, args: Array.<string>, silent: boolean}}
 */
cluster.Settings;

/**
 * @type {cluster.Settings}
 */
cluster.settings;

/**
 * @type {boolean}
 */
cluster.isMaster;

/**
 * @type {boolean}
 */
cluster.isWorker;

/**
 * @param {cluster.Settings=} settings
 */
cluster.setupMaster = function(settings) {};

/**
 * @param {Object.<string,*>} env
 * @return {cluster.Worker}
 */
cluster.fork = function(env) {};

/**
 * @param {function()=} callback
 */
cluster.disconnect = function(callback) {};

/**
 * @type {?cluster.Worker}
 */
cluster.worker;

/**
 * @type {?Object.<string,cluster.Worker>}
 */
cluster.workers;

/**
 * @constructor
 * @extends events.EventEmitter
 */
cluster.Worker = function() {};

/**
 * @type {string}
 */
cluster.Worker.prototype.id;

/**
 * @type {child_process.ChildProcess}
 */
cluster.Worker.prototype.process;

/**
 * @type {boolean}
 */
cluster.Worker.prototype.suicide;

/**
 * @param {Object} message
 * @param {*=} sendHandle
 */
cluster.Worker.prototype.send = function(message, sendHandle) {};

/**
 */
cluster.Worker.prototype.destroy = function() {};

/**
 */
cluster.Worker.prototype.disconnect = function() {};
<|endoftext|>


<commit_before>require('moonstone');

/**
* Contains the declaration for the {@link module:moonstone/ExpandableListItem~ExpandableListItem} kind.
* @module moonstone/ExpandableListItem
*/

var
	kind = require('enyo/kind'),
	Component = require('enyo/Component'),
	Control = require('enyo/Control'),
	Group = require('enyo/Group');

var
	Spotlight = require('spotlight');

var
	Item = require('../Item');

var
	ExpandableListItemHeader = require('./ExpandableListItemHeader'),
	ExpandableListItemDrawer = require('./ExpandableListItemDrawer');

/**
* {@link module:moonstone/ExpandableListItem~ExpandableListItem}, which extends {@link module:moonstone/Item~Item}, displays a header
* while also allowing additional content to be stored in an {@link module:enyo/Drawer~Drawer}. When
* the header is selected, the drawer opens below. To close the drawer, tap on the
* header text or navigate (via 5-way) back to the top of the drawer.
*
* The control's child components may be of any kind; by default, they are
* instances of `moonstone/Item`.
*
* ```javascript
* 	var
* 		kind = require('enyo/kind'),
* 		ExpandableListItem = require('moonstone/ExpandableListItem');
*
* 	{kind: ExpandableListItem, content: 'A Countries', components: [
* 		{content: 'Algeria'},
* 		{content: 'Argentina'},
* 		{content: 'Australia'}
* 	]},
* 	{kind: ExpandableListItem, content: 'B Countries', components: [
* 		{content: 'Belgium'},
* 		{content: 'Bolivia'},
* 		{content: 'Brazil'}
* 	]}
* ```
*
* When multiple ExpandableListItems are used in a group, only one may be open at
* a given time.
*
* ```javascript
* 	var
* 		kind = require('enyo/kind'),
* 		Group = require('enyo/Group'),
* 		ExpandableListItem = require('moonstone/ExpandableListItem');
*
* 	{kind: Group, highlander: true, components: [
* 		{kind: ExpandableListItem,  open: true,
* 			content: 'This is a grouped ExpandableListItem', components: [
* 				{content: 'Item One'},
* 				{content: 'Item Two'}
* 		]
* 	},
* 	{kind: ExpandableListItem,
* 		content: 'This is another grouped ExpandableListItem', components: [
* 			{content: 'Item Three'},
* 			{content: 'Item Four'}
* 		]
* 	},
* 	{kind: ExpandableListItem,
* 		content: 'This is yet another grouped ExpandableListItem', components: [
* 			{content: 'Item Five'},
* 			{content: 'Item Six'}
* 		]
* 	}
* ```
*
* @class ExpandableListItem
* @extends module:enyo/Control~Control
* @ui
* @public
*/
module.exports = kind(
	/** @lends module:moonstone/ExpandableListItem~ExpandableListItem.prototype */ {

	/**
	* @private
	*/
	name: 'moon.ExpandableListItem',

	/**
	* @private
	*/
	kind: Control,

	/**
	* @private
	* @lends module:moonstone/ExpandableListItem~ExpandableListItem.prototype
	*/
	published: {

		/**
		* If `true`, the drawer automatically closes when the user navigates to the top of the
		* control; if `false`, the user must select/tap the header to close the drawer.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		autoCollapse: false,

		/**
		* If `true`, the drawer is expanded, showing this item's contents. Use this property
		* (rather than [active]{@link module:moonstone/ExpandableListItem~ExpandableListItem#active}) to set the item's
		* initial state.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		open: false,

		/**
		* Boolean that reflects the value of the [open]{@link module:moonstone/ExpandableListItem~ExpandableListItem#open}
		* property; it is used to support the {@link module:enyo/Group~Group} API for grouping a set of
		* ExpandableListItems in which only one is expanded at a time. Note that the `open`
		* property (not the `active` property) controls the initial state of the picker.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		active: false,

		/**
		* If `true`, the user is prevented from moving {@glossary Spotlight} past the bottom
		* of the drawer (when open) using 5-way controls.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		lockBottom: false,

		/**
		* If `true`, item is shown as disabled and does not generate tap events.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		disabled: false
	},

	/**
	* @private
	*/
	classes: 'moon-expandable-list-item',

	/**
	* @private
	*/
	spotlight: false,

	/**
	* @private
	*/
	defaultKind: Item,

	/**
	* @private
	*/
	drawerComponents: [
		{name: 'client', kind: Group, tag: null}
	],

	/**
	* @private
	*/
	components: [
		{name: 'header', kind: ExpandableListItemHeader, ontap: 'headerTapped'},
		{name: 'drawer', kind: ExpandableListItemDrawer, resizeContainer: false, classes: 'moon-expandable-list-item-client', defaultSpotlightUp: 'header', onSpotlightUp: 'drawerSpotUp', onSpotlightDown: 'drawerSpotDown', onDrawerAnimationEnd: 'drawerAnimationEnd'}
	],

	/**
	* @private
	*/
	bindings: [
		{from: 'active', to: 'open'},
		{from: 'allowHtml', to: '$.header.allowHtml'},
		{from: 'disabled', to: '$.header.disabled'},
		{from: 'content', to: '$.header.label'},
		{from: 'currentValueShowing', to: '$.header.textShowing'},
		{from: 'currentValueText', to: '$.header.text'},

		// Accessibility
		{from: 'accessibilityHint', to: '$.header.accessibilityHint'},
		{from: 'accessibilityLabel', to: '$.header.accessibilityLabel'},
		{from: 'accessibilityDisabled', to: '$.header.accessibilityDisabled'}
	],

	/**
	* @private
	*/
	computed: {
		'currentValueShowing': ['open', 'currentValueText'],
		'currentValueText': ['value']
	},

	/**
	* @private
	*/
	create: function () {
		Control.prototype.create.apply(this, arguments);
		this.openChanged();
		this.disabledChanged();
		this.notifyObservers('value');
	},

	/**
	* @private
	*/
	initComponents: function () {
		var override = {drawer: {components: this.drawerComponents}};
		this.kindComponents = Component.overrideComponents(this.kindComponents, override);
		Control.prototype.initComponents.apply(this, arguments);
	},

	/**
	* Facade for drawer.
	*
	* @private
	*/
	openChanged: function () {
		var open = this.open;
		this.bubble('onActivate', {allowHighlanderDeactivate: true});
		this.addRemoveClass('open', open);
		this.$.drawer.set('open', open);
		this.$.drawer.spotlightDisabled = !open;
		this.set('active', open);
	},

	/**
	* @fires module:enyo/GroupItem~GroupItem#onActivate
	* @private
	*/
	activeChanged: function () {
		this.bubble('onActivate', {allowHighlanderDeactivate:true});
		this.set('open', this.active);
	},

	/**
	* @private
	*/
	disabledChanged: function () {
		var disabled = this.disabled;

		this.addRemoveClass('disabled', disabled);
		if (disabled) {
			this.set('active', false);
		}
	},

	/**
	* Returns `true` if the current value control should be displayed.
	*
	* Note: ExpandableListItem doesn't support currentValue but its subkinds do
	*
	* @private
	*/
	currentValueShowing: function () {
		var text = this.get('currentValueText');
		// If drawer is open or value is blank, don't bother to show the value.
		return (!this.open && (text || text === 0));
	},

	/**
	* Returns the text to be displayed within the current value control
	*
	* Note: ExpandableListItem doesn't support currentValue but its subkinds do
	*
	* @private
	*/
	currentValueText: function () {
		return '';
	},

	/**
	* If closed, opens drawer and highlights first spottable child.
	*
	* @private
	*/
	expandContract: function () {
		if (this.open) {
			this.closeDrawerAndHighlightHeader();
		} else {
			this.toggleActive();
			if (!Spotlight.getPointerMode() && !Spotlight.isFrozen()) {
				var first = Spotlight.getFirstChild(this.$.drawer);
				Spotlight.spot(first);
			}
		}
	},

	/**
	* @private
	*/
	toggleActive: function () {
		this.set('active', !this.open);
	},

	/**
	* @private
	*/
	closeDrawerAndHighlightHeader: function () {
		var current = Spotlight.getPointerMode() ? Spotlight.getLastControl() : Spotlight.getCurrent();

		// If the spotlight is elsewhere, we don't want to hijack it (e.g. after the delay in
		// ExpandablePicker)
		if (!current || current.isDescendantOf(this)) {
			Spotlight.spot(this.$.header);
		}
		this.set('active', false);
	},

	/**
	* @private
	*/
	drawerSpotUp: function (sender, event) {
		if (this.autoCollapse && event.originator == this.$.drawer) {
			this.closeDrawerAndHighlightHeader();
			return true;
		}
	},

	/**
	* @private
	*/
	drawerSpotDown: function (sender, event) {
		if (this.lockBottom && event.originator == this.$.drawer && event._originator) {
			// Spotlight containers redispatch 5-way events with the original event originator
			// saved as _originator which we'll use to respot if lockBottom === true
			Spotlight.spot(event._originator, {direction: 'DOWN'});
			return true;
		}
	},

	/**
	* @private
	*/
	headerTapped: function (sender, event) {
		this.expandContract();
	},

	/**
	* When an expandable is in a group, we only want the opening control to scroll into view so that
	* event only fires when the drawer is open. Otherwise, we just request the scroller update its
	* bounds so that an expandable at the bottom of a scroller does not leave extra whitespace when
	* it closes.
	*
	* @fires module:moonstone/Scroller~Scroller#onRequestScrollIntoView
	* @fires module:moonstone/Scroller~Scroller#onRequestSetupBounds
	* @private
	*/
	drawerAnimationEnd: function () {
		if (this.$.drawer.open) {
			this.bubble('onRequestScrollIntoView', {scrollInPointerMode: true});
		} else {
			this.bubble('onRequestSetupBounds');
		}
		return true;
	},

	// Accessibility

	/**
	* ExpandableListItem has not spotlight, its child (this.$.header) is spottable item,
	* so we remove unneccessary aria-related attributes.
	*
	* @private
	*/
	ariaObservers: [
		{path: ['accessibilityLabel', 'accessibilityHint'], method: function () {
			this.setAriaAttribute('aria-label', null);
		}}
	]
});
<commit_msg>PLAT-15389: Add back key support to Expandables.<commit_after>require('moonstone');

/**
* Contains the declaration for the {@link module:moonstone/ExpandableListItem~ExpandableListItem} kind.
* @module moonstone/ExpandableListItem
*/

var
	kind = require('enyo/kind'),
	Component = require('enyo/Component'),
	Control = require('enyo/Control'),
	EnyoHistory = require('enyo/History'),
	Group = require('enyo/Group');

var
	Spotlight = require('spotlight');

var
	HistorySupport = require('../HistorySupport'),
	Item = require('../Item');

var
	ExpandableListItemHeader = require('./ExpandableListItemHeader'),
	ExpandableListItemDrawer = require('./ExpandableListItemDrawer');

/**
* {@link module:moonstone/ExpandableListItem~ExpandableListItem}, which extends {@link module:moonstone/Item~Item}, displays a header
* while also allowing additional content to be stored in an {@link module:enyo/Drawer~Drawer}. When
* the header is selected, the drawer opens below. To close the drawer, tap on the
* header text or navigate (via 5-way) back to the top of the drawer.
*
* The control's child components may be of any kind; by default, they are
* instances of `moonstone/Item`.
*
* ```javascript
* 	var
* 		kind = require('enyo/kind'),
* 		ExpandableListItem = require('moonstone/ExpandableListItem');
*
* 	{kind: ExpandableListItem, content: 'A Countries', components: [
* 		{content: 'Algeria'},
* 		{content: 'Argentina'},
* 		{content: 'Australia'}
* 	]},
* 	{kind: ExpandableListItem, content: 'B Countries', components: [
* 		{content: 'Belgium'},
* 		{content: 'Bolivia'},
* 		{content: 'Brazil'}
* 	]}
* ```
*
* When multiple ExpandableListItems are used in a group, only one may be open at
* a given time.
*
* ```javascript
* 	var
* 		kind = require('enyo/kind'),
* 		Group = require('enyo/Group'),
* 		ExpandableListItem = require('moonstone/ExpandableListItem');
*
* 	{kind: Group, highlander: true, components: [
* 		{kind: ExpandableListItem,  open: true,
* 			content: 'This is a grouped ExpandableListItem', components: [
* 				{content: 'Item One'},
* 				{content: 'Item Two'}
* 		]
* 	},
* 	{kind: ExpandableListItem,
* 		content: 'This is another grouped ExpandableListItem', components: [
* 			{content: 'Item Three'},
* 			{content: 'Item Four'}
* 		]
* 	},
* 	{kind: ExpandableListItem,
* 		content: 'This is yet another grouped ExpandableListItem', components: [
* 			{content: 'Item Five'},
* 			{content: 'Item Six'}
* 		]
* 	}
* ```
*
* @class ExpandableListItem
* @extends module:enyo/Control~Control
* @ui
* @public
*/
module.exports = kind(
	/** @lends module:moonstone/ExpandableListItem~ExpandableListItem.prototype */ {

	/**
	* @private
	*/
	name: 'moon.ExpandableListItem',

	/**
	* @private
	*/
	kind: Control,

	/**
	* @private
	*/
	mixins: [HistorySupport],

	/**
	* @private
	* @lends module:moonstone/ExpandableListItem~ExpandableListItem.prototype
	*/
	published: {

		/**
		* If `true`, the drawer automatically closes when the user navigates to the top of the
		* control; if `false`, the user must select/tap the header to close the drawer.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		autoCollapse: false,

		/**
		* If `true`, the drawer is expanded, showing this item's contents. Use this property
		* (rather than [active]{@link module:moonstone/ExpandableListItem~ExpandableListItem#active}) to set the item's
		* initial state.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		open: false,

		/**
		* Boolean that reflects the value of the [open]{@link module:moonstone/ExpandableListItem~ExpandableListItem#open}
		* property; it is used to support the {@link module:enyo/Group~Group} API for grouping a set of
		* ExpandableListItems in which only one is expanded at a time. Note that the `open`
		* property (not the `active` property) controls the initial state of the picker.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		active: false,

		/**
		* If `true`, the user is prevented from moving {@glossary Spotlight} past the bottom
		* of the drawer (when open) using 5-way controls.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		lockBottom: false,

		/**
		* If `true`, item is shown as disabled and does not generate tap events.
		*
		* @type {Boolean}
		* @default false
		* @public
		*/
		disabled: false
	},

	/**
	* @private
	*/
	classes: 'moon-expandable-list-item',

	/**
	* @private
	*/
	spotlight: 'container',

	/**
	* @private
	*/
	defaultKind: Item,

	/**
	* @private
	*/
	handlers: {
		onSpotlightContainerEnter: 'handleContainerEnter',
		onSpotlightContainerLeave: 'handleContainerLeave'
	},

	/**
	* @private
	*/
	drawerComponents: [
		{name: 'client', kind: Group, tag: null}
	],

	/**
	* @private
	*/
	components: [
		{name: 'header', kind: ExpandableListItemHeader, ontap: 'headerTapped'},
		{name: 'drawer', kind: ExpandableListItemDrawer, resizeContainer: false, classes: 'moon-expandable-list-item-client', defaultSpotlightUp: 'header', onSpotlightUp: 'drawerSpotUp', onSpotlightDown: 'drawerSpotDown', onDrawerAnimationEnd: 'drawerAnimationEnd'}
	],

	/**
	* @private
	*/
	bindings: [
		{from: 'active', to: 'open'},
		{from: 'allowHtml', to: '$.header.allowHtml'},
		{from: 'disabled', to: '$.header.disabled'},
		{from: 'content', to: '$.header.label'},
		{from: 'currentValueShowing', to: '$.header.textShowing'},
		{from: 'currentValueText', to: '$.header.text'},

		// Accessibility
		{from: 'accessibilityHint', to: '$.header.accessibilityHint'},
		{from: 'accessibilityLabel', to: '$.header.accessibilityLabel'},
		{from: 'accessibilityDisabled', to: '$.header.accessibilityDisabled'}
	],

	/**
	* @private
	*/
	computed: {
		'currentValueShowing': ['open', 'currentValueText'],
		'currentValueText': ['value']
	},

	/**
	* @private
	*/
	create: function () {
		Control.prototype.create.apply(this, arguments);
		this.openChanged();
		this.disabledChanged();
		this.notifyObservers('value');
	},

	/**
	* @private
	*/
	initComponents: function () {
		var override = {drawer: {components: this.drawerComponents}};
		this.kindComponents = Component.overrideComponents(this.kindComponents, override);
		Control.prototype.initComponents.apply(this, arguments);
	},

	/**
	* Facade for drawer.
	*
	* @private
	*/
	openChanged: function () {
		var open = this.open;
		this.bubble('onActivate', {allowHighlanderDeactivate: true});
		this.addRemoveClass('open', open);
		this.$.drawer.set('open', open);
		this.$.drawer.spotlightDisabled = !open;
		this.set('active', open);
	},

	/**
	* @fires module:enyo/GroupItem~GroupItem#onActivate
	* @private
	*/
	activeChanged: function () {
		this.bubble('onActivate', {allowHighlanderDeactivate:true});
		this.set('open', this.active);
	},

	/**
	* @private
	*/
	disabledChanged: function () {
		var disabled = this.disabled;

		this.addRemoveClass('disabled', disabled);
		if (disabled) {
			this.set('active', false);
		}
	},

	/**
	* Returns `true` if the current value control should be displayed.
	*
	* Note: ExpandableListItem doesn't support currentValue but its subkinds do
	*
	* @private
	*/
	currentValueShowing: function () {
		var text = this.get('currentValueText');
		// If drawer is open or value is blank, don't bother to show the value.
		return (!this.open && (text || text === 0));
	},

	/**
	* Returns the text to be displayed within the current value control
	*
	* Note: ExpandableListItem doesn't support currentValue but its subkinds do
	*
	* @private
	*/
	currentValueText: function () {
		return '';
	},

	/**
	* If closed, opens drawer and highlights first spottable child.
	*
	* @private
	*/
	expandContract: function () {
		if (this.open) {
			this.closeDrawerAndHighlightHeader();
		} else {
			this.toggleActive();
			if (!Spotlight.getPointerMode() && !Spotlight.isFrozen()) {
				var first = Spotlight.getFirstChild(this.$.drawer);
				Spotlight.spot(first);
			}
			// As we could have entered the expandable when it was closed, we need to explicitly add
			// the history entry when it is opened.
			if (this.allowBackKey) {
				this.pushBackHistory();
			}
		}
	},

	/**
	* @private
	*/
	toggleActive: function () {
		this.set('active', !this.open);
	},

	/**
	* @private
	*/
	closeDrawerAndHighlightHeader: function () {
		var current = Spotlight.getPointerMode() ? Spotlight.getLastControl() : Spotlight.getCurrent();

		if (this.allowBackKey && !EnyoHistory.isProcessing()) {
			EnyoHistory.drop();
		}

		// If the spotlight is elsewhere, we don't want to hijack it (e.g. after the delay in
		// ExpandablePicker)
		if (!current || current.isDescendantOf(this)) {
			Spotlight.spot(this.$.header);
		}
		this.set('active', false);
	},

	/**
	* @private
	*/
	drawerSpotUp: function (sender, ev) {
		if (this.autoCollapse && ev.originator == this.$.drawer) {
			this.closeDrawerAndHighlightHeader();
			return true;
		}
	},

	/**
	* @private
	*/
	drawerSpotDown: function (sender, ev) {
		if (this.lockBottom && ev.originator == this.$.drawer && ev._originator) {
			// Spotlight containers redispatch 5-way events with the original event originator
			// saved as _originator which we'll use to respot if lockBottom === true
			Spotlight.spot(ev._originator, {direction: 'DOWN'});
			return true;
		}
	},

	handleContainerEnter: function (sender, ev) {
		// We only want to add a history entry if the expandable is open.
		if (this.allowBackKey && this.open) {
			this.pushBackHistory();
		}
	},

	handleContainerLeave: function (sender, ev) {
		// We guard against dropping a history entry if the expandable is closed, since this should
		// have already been handled when the expandable was collapsed.
		if (this.allowBackKey && this.open) {
			EnyoHistory.drop();
		}
	},

	/**
	* @private
	*/
	headerTapped: function (sender, ev) {
		this.expandContract();
	},

	/**
	* When an expandable is in a group, we only want the opening control to scroll into view so that
	* event only fires when the drawer is open. Otherwise, we just request the scroller update its
	* bounds so that an expandable at the bottom of a scroller does not leave extra whitespace when
	* it closes.
	*
	* @fires module:moonstone/Scroller~Scroller#onRequestScrollIntoView
	* @fires module:moonstone/Scroller~Scroller#onRequestSetupBounds
	* @private
	*/
	drawerAnimationEnd: function () {
		if (this.$.drawer.open) {
			this.bubble('onRequestScrollIntoView', {scrollInPointerMode: true});
		} else {
			this.bubble('onRequestSetupBounds');
		}
		return true;
	},

	/**
	* @private
	*/
	backKeyHandler: function () {
		var current = Spotlight.getCurrent();

		// In the case where Spotlight focus is not on one of the items in the expandable, but there
		// was still an entry history from this control, we must be in a situation where the pointer
		// has moved away from the control and we have yet to spot another item. We should then
		// effectively "pass on" the back action by calling the "pop" method of History.
		if (current && current.isDescendantOf(this)) {
			this.closeDrawerAndHighlightHeader();
		} else {
			EnyoHistory.pop();
		}

		return true;
	},

	// Accessibility

	/**
	* ExpandableListItem has not spotlight, its child (this.$.header) is spottable item,
	* so we remove unneccessary aria-related attributes.
	*
	* @private
	*/
	ariaObservers: [
		{path: ['accessibilityLabel', 'accessibilityHint'], method: function () {
			this.setAriaAttribute('aria-label', null);
		}}
	]
});
<|endoftext|>


<commit_before>
describe("Commands", function() {
	var c, iq;
	beforeEach(function() {
		c = mockConnection();
		iq = {to: 'n@d/r2', from: 'n@d/r1', type: 'get', id: 'abc'};
	});

	it("disco#info includes command feature", function() {
		var req = $iq(iq).c('query', { xmlns: Strophe.NS.DISCO_INFO});
		spyon(c,'send',function(res) {
			expect(res.find('feature:eq(2)').attr('var')).toEqual(Strophe.NS.COMMANDS);
		});
		receive(c,req);
	});

	it("disco#items includes added commands", function() {
		c.cmds.add({ node: 'a'});
		var req = $iq(iq).c('query', { xmlns: Strophe.NS.DISCO_ITEMS, node: Strophe.NS.COMMANDS});
		spyon(c,'send',function(res) {
			expect(res.find('item:eq(0)').attr('jid')).toEqual('n@d/r2');
			expect(res.find('item:eq(0)').attr('node')).toEqual('a');
		});
		receive(c,req);
	});

	it("responds to command execution", function() {
		c.cmds.add({ node: 'a', name: 'aName' });
		var iq = $iq({type: 'set'}).c('command', {action: 'execute', node: 'a', xmlns: Strophe.NS.COMMANDS});
		spyon(c,'send',function(res) {
			expect(res.find('command').attr('status')).toEqual('completed');
		});
		receive(c,iq);
	});

	it("responds with notFound for unknown command", function() {
		var iq = $iq({type: 'set'}).c('command', { action: 'execute', node: 'a', xmlns: Strophe.NS.COMMANDS });
		spyon(c,'send',function(res) {
			expect(res.find('item-not-found').length).toEqual(1);
		});
		receive(c,iq);
	});

	it("can execute command", function() {
		spyon(c,'send',function(req) {
			expect(req.find('command').attr('node')).toEqual('aCmd');
			expect(req.find('command').attr('action')).toEqual('execute');
		});
		c.cmds.execute('n@d/r','aCmd');
		expect(c.send).toHaveBeenCalled();
	});
});

<commit_msg>added test case for dynamically building up request message with data<commit_after>describe("Commands", function() {
	var c, iq;
	beforeEach(function() {
		c = mockConnection();
		iq = {to: 'n@d/r2', from: 'n@d/r1', type: 'get', id: 'abc'};
	});

	it("disco#info includes command feature", function() {
		var req = $iq(iq).c('query', { xmlns: Strophe.NS.DISCO_INFO});
		spyon(c,'send',function(res) {
			expect(res.find('feature:eq(2)').attr('var')).toEqual(Strophe.NS.COMMANDS);
		});
		receive(c,req);
	});

	it("disco#items includes added commands", function() {
		c.cmds.add({ node: 'a'});
		var req = $iq(iq).c('query', { xmlns: Strophe.NS.DISCO_ITEMS, node: Strophe.NS.COMMANDS});
		spyon(c,'send',function(res) {
			expect(res.find('item:eq(0)').attr('jid')).toEqual('n@d/r2');
			expect(res.find('item:eq(0)').attr('node')).toEqual('a');
		});
		receive(c,req);
	});

	it("responds to command execution", function() {
		c.cmds.add({ node: 'a', name: 'aName' });
		var iq = $iq({type: 'set'}).c('command', {action: 'execute', node: 'a', xmlns: Strophe.NS.COMMANDS});
		spyon(c,'send',function(res) {
			expect(res.find('command').attr('status')).toEqual('completed');
		});
		receive(c,iq);
	});

	it("responds with notFound for unknown command", function() {
		var iq = $iq({type: 'set'}).c('command', { action: 'execute', node: 'a', xmlns: Strophe.NS.COMMANDS });
		spyon(c,'send',function(res) {
			expect(res.find('item-not-found').length).toEqual(1);
		});
		receive(c,iq);
	});

	it("can execute command", function() {
		spyon(c,'send',function(req) {
			expect(req.find('command').attr('node')).toEqual('aCmd');
			expect(req.find('command').attr('action')).toEqual('execute');
		});
		c.cmds.execute('n@d/r','aCmd');
		expect(c.send).toHaveBeenCalled();
	});

	it("can execute command with data", function() {
		c.cmds.add({name: 'foo', node: 'foo', item: 'foo'});
		spyon(c,'send',function(req) {
			expect(req.find('command').attr('node')).toEqual('foo');
			expect(req.find('command').attr('action')).toEqual('execute');
			expect(req.find('foo').size()).toEqual(2);
			// somethings wronte with SpecHelper#createRequest
			expect(req.find('command foo').size()).toEqual(0);
		});
		c.cmds.execute('n@d/r','foo', ['foo','bar']);
		expect(c.send).toHaveBeenCalled();
	});
});

<|endoftext|>


<commit_before>angular.module('examples', [])

.factory('formPostData', ['$document', function($document) {
  return function(url, fields) {
    var form = angular.element('<form style="display: none;" method="post" action="' + url + '" target="_blank"></form>');
    angular.forEach(fields, function(value, name) {
      var input = angular.element('<input type="hidden" name="' +  name + '">');
      input.attr('value', value);
      form.append(input);
    });
    $document.find('body').append(form);
    form[0].submit();
    form.remove();
  };
}])

.factory('openPlunkr', ['formPostData', '$http', '$q', function(formPostData, $http, $q) {
  return function(exampleFolder) {

    var exampleName = 'AngularJS Example';

    // Load the manifest for the example
    $http.get(exampleFolder + '/manifest.json')
      .then(function(response) {
        return response.data;
      })
      .then(function(manifest) {
        var filePromises = [];

        // Build a pretty title for the Plunkr
        var exampleNameParts = manifest.name.split('-');
        exampleNameParts.unshift('AngularJS');
        angular.forEach(exampleNameParts, function(part, index) {
          exampleNameParts[index] = part.charAt(0).toUpperCase() + part.substr(1);
        });
        exampleName = exampleNameParts.join(' - ');

        angular.forEach(manifest.files, function(filename) {
          filePromises.push($http.get(exampleFolder + '/' + filename)
            .then(function(response) {

              // The manifests provide the production index file but Plunkr wants
              // a straight index.html
              if (filename === "index-production.html") {
                filename = "index.html"
              }

              return {
                name: filename,
                content: response.data
              };
            }));
        });
        return $q.all(filePromises);
      })
      .then(function(files) {
        var postData = {};

        angular.forEach(files, function(file) {
          postData['files[' + file.name + ']'] = file.content;
        });

        postData['tags[0]'] = "angularjs";
        postData['tags[1]'] = "example";
        postData.private = true;
        postData.description = exampleName;

        formPostData('http://plnkr.co/edit/?p=preview', postData);
      });
  };
}]);<commit_msg>docs(examples): don't attempt to convert example content from JSON<commit_after>angular.module('examples', [])

.factory('formPostData', ['$document', function($document) {
  return function(url, fields) {
    var form = angular.element('<form style="display: none;" method="post" action="' + url + '" target="_blank"></form>');
    angular.forEach(fields, function(value, name) {
      var input = angular.element('<input type="hidden" name="' +  name + '">');
      input.attr('value', value);
      form.append(input);
    });
    $document.find('body').append(form);
    form[0].submit();
    form.remove();
  };
}])

.factory('openPlunkr', ['formPostData', '$http', '$q', function(formPostData, $http, $q) {
  return function(exampleFolder) {

    var exampleName = 'AngularJS Example';

    // Load the manifest for the example
    $http.get(exampleFolder + '/manifest.json')
      .then(function(response) {
        return response.data;
      })
      .then(function(manifest) {
        var filePromises = [];

        // Build a pretty title for the Plunkr
        var exampleNameParts = manifest.name.split('-');
        exampleNameParts.unshift('AngularJS');
        angular.forEach(exampleNameParts, function(part, index) {
          exampleNameParts[index] = part.charAt(0).toUpperCase() + part.substr(1);
        });
        exampleName = exampleNameParts.join(' - ');

        angular.forEach(manifest.files, function(filename) {
          filePromises.push($http.get(exampleFolder + '/' + filename, { transformResponse: [] })
            .then(function(response) {

              // The manifests provide the production index file but Plunkr wants
              // a straight index.html
              if (filename === "index-production.html") {
                filename = "index.html"
              }

              return {
                name: filename,
                content: response.data
              };
            }));
        });
        return $q.all(filePromises);
      })
      .then(function(files) {
        var postData = {};

        angular.forEach(files, function(file) {
          postData['files[' + file.name + ']'] = file.content;
        });

        postData['tags[0]'] = "angularjs";
        postData['tags[1]'] = "example";
        postData.private = true;
        postData.description = exampleName;

        formPostData('http://plnkr.co/edit/?p=preview', postData);
      });
  };
}]);<|endoftext|>


<commit_before>/*
  Copyright 2010-2017 BusinessCode GmbH, Germany

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

if( typeof bcdui=="undefined" )
    bcdui = {};

/**
 * Utility package for raw data visualization used for debugging purposes.
 * @namespace bcdui.widget.visualizeXml
 */
bcdui.widget.visualizeXml =
/** @lends bcdui.widget.visualizeXml */
{

  /**
   * @private
   */
  _visualizeXml_CollapsSign : '',

  /**
   * @private
   */
  _getClassName: function( elem ) {
    if( elem && elem.className )
      return elem.className;
    else if( elem && elem.getAttribute)
      return elem.getAttribute("class");
    else
      return "";
  },

  /**
   * @private
   */
  _setClassName: function( elem, classname ) {
    if( elem.className )
      elem.className = classname;
    else
      elem.setAttribute("class",classname);
  },

  /**
   * @private
   */
  _f: function(e){
     if (bcdui.widget.visualizeXml._getClassName(e)=="visXml_ci") {
       if (e.firstChild.nodeValue.indexOf("\n")>0) _fix(e,"visXml_cb");
     }
     if (bcdui.widget.visualizeXml._getClassName(e)=="visXml_di") {
       if (e.firstChild.nodeValue.indexOf("\n")>0) _fix(e,"visXml_db");
     } e.id="";
  },

  /**
   * @private
   */
  _fix: function(e,cl){
    bcdui.widget.visualizeXml._setClassName(e,cl);
    e.style.display="";
    j=e.parentNode.firstChild;
    bcdui.widget.visualizeXml._setClassName(j,"visXml_c");
    k=j.firstChild;
    k.style.visibility="visible";
    k.href="#";
  },

  /**
   * @private
   */
  _ch: function(e) {
    mark=e.firstChild.firstChild;
    mark = e.getElementsByTagName("A")[0];
    if( !mark.firstChild )
      mark.appendChild(document.createTextNode(''));
    mark = mark.firstChild;
    if (mark.nodeValue.match(/\+$/)) {
      mark.nodeValue = bcdui.widget.visualizeXml._visualizeXml_CollapsSign;
      for (var i=1;i<e.childNodes.length;i++) {
        e.childNodes.item(i).style.display="";
      }
    }
    else {
      mark.nodeValue=mark.nodeValue.substring(0,mark.nodeValue.length-1)+"+";
      for (var i=1;i<e.childNodes.length;i++) {
        e.childNodes.item(i).style.display="none";
      }
    }
  },

  /**
   * @private
   */
  _ch2: function(e) {
    mark=e.firstChild.firstChild;
    mark = e.getElementsByTagName("A")[0];
    if( !mark.firstChild )
      mark.appendChild(document.createTextNode(''));
    mark = mark.firstChild;
    contents=e.childNodes.item(1);
    if (mark.nodeValue.match(/\+$/)) {
      mark.nodeValue = mark.nodeValue.substring(0,mark.nodeValue.length-1)+ (bcdui.widget.visualizeXml._visualizeXml_CollapsSign.length!=0?bcdui.widget.visualizeXml._visualizeXml_CollapsSign:" ");
      if (bcdui.widget.visualizeXml._getClassName(contents)=="visXml_db"||bcdui.widget.visualizeXml._getClassName(contents)=="visXml_cb") {
        contents.style.display="";
      }
      else {
        contents.style.display="none";
      }
    }
    else {
      mark.nodeValue=mark.nodeValue.substring(0,mark.nodeValue.length-bcdui.widget.visualizeXml._visualizeXml_CollapsSign.length)+"+";
      contents.style.display="none";
    }
  },

  /**
   * @private
   */
  _handleClick: function(evt) {
    var e = window.event ? window.event.srcElement : evt.originalTarget;
    if (bcdui.widget.visualizeXml._getClassName(e)!="visXml_c") {
      e=e.parentNode;
      if (bcdui.widget.visualizeXml._getClassName(e)!="visXml_c") {
        return;
      }
    }
    e=e.parentNode;
    if (bcdui.widget.visualizeXml._getClassName(e)=="visXml_e") {
      bcdui.widget.visualizeXml._ch(e);
    }
    if (bcdui.widget.visualizeXml._getClassName(e)=="visXml_k") {
      bcdui.widget.visualizeXml._ch2(e);
    }
  },

  /**
   * Visualiazes data of a model / data provider
   *
   * @param {object}                  args                The argument map containing the following elements:
   * @param {targetHtmlRef}           args.targetHtml     Id of the html element where to show the output.
   * @param {string}                  [args.title]        Title of the content box; if not provided, the title is set to the ID of the visualized model.
   * @param {string}                  [args.idRef]        Id of the model to be visualized
   * @param {bcdui.core.DataProvider} [args.inputModel]   Instead of an id, the model can be provided directly
   */
  visualizeModel: function(args)
  {
    args = bcdui.factory._xmlArgs( args, bcdui.factory.validate.core._schema_visualizeModel_args);
    args.targetHtml = args.targetHTMLElementId = bcdui.util._getTargetHtml(args, "visualizeModel_");
    bcdui.factory.validate.jsvalidation._validateArgs(args, bcdui.factory.validate.core._schema_visualizeModel_args);

    var rendererId = args.idRef + "_visualizeModelRenderer";
    var preId = args.idRef +"_visualizeModelTargetElementId";

    args.title = args.title || args.idRef || args.inputModel.id;
    jQuery("#" + args.targetHTMLElementId).append("" + args.title + "");

    jQuery("#" + args.targetHTMLElementId).append('

');

    bcdui.factory.createRenderer({
        id: rendererId
      , targetHTMLElementId: args.idRef +"_visualizeModelTargetElementId"
      , chain: ""
      , url: bcdui.contextPath + "/bcdui/js/widget/visualizeXml/visualizeXmlCaller.xslt"
      , inputModel: args.idRef ? {refId: args.idRef} : args.inputModel
    });
    bcdui.factory.addDataListener({
      idRef: args.idRef ? {refId: args.idRef} : args.inputModel.id
    , listener: function() { bcdui.factory.objectRegistry.getObject(rendererId).execute(true); }
    });
  }
}<commit_msg>visualizeModel.js: visualizeModel fix<commit_after>/*
  Copyright 2010-2017 BusinessCode GmbH, Germany

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

if( typeof bcdui=="undefined" )
    bcdui = {};

/**
 * Utility package for raw data visualization used for debugging purposes.
 * @namespace bcdui.widget.visualizeXml
 */
bcdui.widget.visualizeXml =
/** @lends bcdui.widget.visualizeXml */
{

  /**
   * @private
   */
  _visualizeXml_CollapsSign : '',

  /**
   * @private
   */
  _getClassName: function( elem ) {
    if( elem && elem.className )
      return elem.className;
    else if( elem && elem.getAttribute)
      return elem.getAttribute("class");
    else
      return "";
  },

  /**
   * @private
   */
  _setClassName: function( elem, classname ) {
    if( elem.className )
      elem.className = classname;
    else
      elem.setAttribute("class",classname);
  },

  /**
   * @private
   */
  _f: function(e){
     if (bcdui.widget.visualizeXml._getClassName(e)=="visXml_ci") {
       if (e.firstChild.nodeValue.indexOf("\n")>0) _fix(e,"visXml_cb");
     }
     if (bcdui.widget.visualizeXml._getClassName(e)=="visXml_di") {
       if (e.firstChild.nodeValue.indexOf("\n")>0) _fix(e,"visXml_db");
     } e.id="";
  },

  /**
   * @private
   */
  _fix: function(e,cl){
    bcdui.widget.visualizeXml._setClassName(e,cl);
    e.style.display="";
    j=e.parentNode.firstChild;
    bcdui.widget.visualizeXml._setClassName(j,"visXml_c");
    k=j.firstChild;
    k.style.visibility="visible";
    k.href="#";
  },

  /**
   * @private
   */
  _ch: function(e) {
    mark=e.firstChild.firstChild;
    mark = e.getElementsByTagName("A")[0];
    if( !mark.firstChild )
      mark.appendChild(document.createTextNode(''));
    mark = mark.firstChild;
    if (mark.nodeValue.match(/\+$/)) {
      mark.nodeValue = bcdui.widget.visualizeXml._visualizeXml_CollapsSign;
      for (var i=1;i<e.childNodes.length;i++) {
        e.childNodes.item(i).style.display="";
      }
    }
    else {
      mark.nodeValue=mark.nodeValue.substring(0,mark.nodeValue.length-1)+"+";
      for (var i=1;i<e.childNodes.length;i++) {
        e.childNodes.item(i).style.display="none";
      }
    }
  },

  /**
   * @private
   */
  _ch2: function(e) {
    mark=e.firstChild.firstChild;
    mark = e.getElementsByTagName("A")[0];
    if( !mark.firstChild )
      mark.appendChild(document.createTextNode(''));
    mark = mark.firstChild;
    contents=e.childNodes.item(1);
    if (mark.nodeValue.match(/\+$/)) {
      mark.nodeValue = mark.nodeValue.substring(0,mark.nodeValue.length-1)+ (bcdui.widget.visualizeXml._visualizeXml_CollapsSign.length!=0?bcdui.widget.visualizeXml._visualizeXml_CollapsSign:" ");
      if (bcdui.widget.visualizeXml._getClassName(contents)=="visXml_db"||bcdui.widget.visualizeXml._getClassName(contents)=="visXml_cb") {
        contents.style.display="";
      }
      else {
        contents.style.display="none";
      }
    }
    else {
      mark.nodeValue=mark.nodeValue.substring(0,mark.nodeValue.length-bcdui.widget.visualizeXml._visualizeXml_CollapsSign.length)+"+";
      contents.style.display="none";
    }
  },

  /**
   * @private
   */
  _handleClick: function(evt) {
    var e = window.event ? window.event.srcElement : evt.originalTarget;
    if (bcdui.widget.visualizeXml._getClassName(e)!="visXml_c") {
      e=e.parentNode;
      if (bcdui.widget.visualizeXml._getClassName(e)!="visXml_c") {
        return;
      }
    }
    e=e.parentNode;
    if (bcdui.widget.visualizeXml._getClassName(e)=="visXml_e") {
      bcdui.widget.visualizeXml._ch(e);
    }
    if (bcdui.widget.visualizeXml._getClassName(e)=="visXml_k") {
      bcdui.widget.visualizeXml._ch2(e);
    }
  },

  /**
   * Visualiazes data of a model / data provider
   *
   * @param {object}                  args                The argument map containing the following elements:
   * @param {targetHtmlRef}           args.targetHtml     Id of the html element where to show the output.
   * @param {string}                  [args.title]        Title of the content box; if not provided, the title is set to the ID of the visualized model.
   * @param {string}                  [args.idRef]        Id of the model to be visualized
   * @param {bcdui.core.DataProvider} [args.inputModel]   Instead of an id, the model can be provided directly
   */
  visualizeModel: function(args)
  {
    args = bcdui.factory._xmlArgs( args, bcdui.factory.validate.core._schema_visualizeModel_args);
    args.targetHtml = args.targetHTMLElementId = bcdui.util._getTargetHtml(args, "visualizeModel_");
    bcdui.factory.validate.jsvalidation._validateArgs(args, bcdui.factory.validate.core._schema_visualizeModel_args);

    var preId = (args.idRef||args.inputModel.id) +"_visualizeModelTargetElementId";

    args.title = args.title || args.idRef || args.inputModel.id;
    jQuery("#" + args.targetHTMLElementId).append("" + args.title + "");

    jQuery("#" + args.targetHTMLElementId).append('

');

    var rendererId = preId + "_renderer";
    bcdui.factory.createRenderer({
      id : rendererId
      , targetHtml: preId
      , url: bcdui.contextPath + "/bcdui/js/widget/visualizeXml/visualizeXmlCaller.xslt"
      , inputModel: args.idRef ? {refId: args.idRef} : args.inputModel
    });
    // update renderer on model updates
    if(args.inputModel){
      args.inputModel.onChange(function(){
        bcdui.factory.objectRegistry.getObject(rendererId).execute();
      });
    }else{
      bcdui.factory.addDataListener({
        idRef: args.idRef
        , listener: function() { bcdui.factory.objectRegistry.getObject(rendererId).execute(); }
      });
    }
  }
}<|endoftext|>


<commit_before>const path = require('path');
const chalk = require('chalk');
const read = require('readdir-recursive');

class CommandManager {

    constructor(bot) {
        this.bot = bot;
        this._commands = [];
        this._categories = [];
    }

    _validateCommand(object) {
        if (typeof object !== 'object')
            return 'command setup is invalid';
        if (typeof object.run !== 'function')
            return 'run function is missing';
        if (typeof object.info !== 'object')
            return 'info object is missing';
        if (typeof object.info.name !== 'string')
            return 'info object is missing a valid name field';
        return null;
    }

    loadCommands(folder) {
        this._commands = [];
        this._categories = [];

        var bot = this.bot;

        read.fileSync(folder).forEach(file => {
            file = file.substr(folder.length + 1);
            var basename = file.substr(file.lastIndexOf('/') + 1);

            if (basename.startsWith('_') || !basename.endsWith('.js')) return;

            var command = require(`${folder}/${file}`);
            var error = this._validateCommand(command);
            if (error) {
                return bot.logger.severe(`Failed to load '${file}': ${chalk.red(error)}`);
            }

            if (!command.category) {
                var category = file.indexOf(path.sep) > -1 ? path.dirname(file) : 'Uncategorized';
                command.info.category = category;

                if (this._categories.indexOf(category) === -1)
                    this._categories.push(category);
            }

            this._commands.push(command);
        });
    }

    all(category) {
        return !category ? this._commands : this._commands.filter(c => c.info.category.toLowerCase() === category.toLowerCase());
    }

    categories() {
        return this._categories;
    }

    get(name) {
        return this.findBy('name', name);
    }

    findBy(key, value) {
        return this._commands.find(c => c.info[key] === value);
    }

    execute(msg, command, args) {
        msg.editEmbed = ((embed) => msg.edit('', { embed })).bind(msg);
        msg.error = ((message, delay) => {
            this.bot.logger.severe(message.toString());
            msg.edit(`:no_entry_sign: ${message || 'An error has occurred!'}`)
                .then(m => m.delete(delay || 2000));
        }).bind(msg);

        try {
            command.run(this.bot, msg, args);
        } catch (e) {
            msg.error(e);
        }
    }

}

module.exports = CommandManager;<commit_msg>Possible fix for Windows<commit_after>const path = require('path');
const chalk = require('chalk');
const read = require('readdir-recursive');

class CommandManager {

    constructor(bot) {
        this.bot = bot;
        this._commands = [];
        this._categories = [];
    }

    _validateCommand(object) {
        if (typeof object !== 'object')
            return 'command setup is invalid';
        if (typeof object.run !== 'function')
            return 'run function is missing';
        if (typeof object.info !== 'object')
            return 'info object is missing';
        if (typeof object.info.name !== 'string')
            return 'info object is missing a valid name field';
        return null;
    }

    loadCommands(folder) {
        this._commands = [];
        this._categories = [];

        var bot = this.bot;

        read.fileSync(folder).forEach(file => {
            file = file.substr(folder.length + 1);
            var basename = path.basename(file);

            if (basename.startsWith('_') || !basename.endsWith('.js')) return;

            var command = require(`${folder}/${file}`);
            var error = this._validateCommand(command);
            if (error) {
                return bot.logger.severe(`Failed to load '${file}': ${chalk.red(error)}`);
            }

            if (!command.category) {
                var category = file.indexOf(path.sep) > -1 ? path.dirname(file) : 'Uncategorized';
                command.info.category = category;

                if (this._categories.indexOf(category) === -1)
                    this._categories.push(category);
            }

            this._commands.push(command);
        });
    }

    all(category) {
        return !category ? this._commands : this._commands.filter(c => c.info.category.toLowerCase() === category.toLowerCase());
    }

    categories() {
        return this._categories;
    }

    get(name) {
        return this.findBy('name', name);
    }

    findBy(key, value) {
        return this._commands.find(c => c.info[key] === value);
    }

    execute(msg, command, args) {
        msg.editEmbed = ((embed) => msg.edit('', { embed })).bind(msg);
        msg.error = ((message, delay) => {
            this.bot.logger.severe(message.toString());
            msg.edit(`:no_entry_sign: ${message || 'An error has occurred!'}`)
                .then(m => m.delete(delay || 2000));
        }).bind(msg);

        try {
            command.run(this.bot, msg, args);
        } catch (e) {
            msg.error(e);
        }
    }

}

module.exports = CommandManager;<|endoftext|>