<commit_before>import React from 'react';
import {without, includes, union, isNull} from 'lodash';

function DefaultItemComponent(props) {
	const item = props.item;
	const selectedItems = props.selectedItems;
	const selected = includes(selectedItems, item);

	return (
		<div title={`${item.label}`}>
			<div
				className={`cp-multi-selector-item__icon ${selected ? "cps-bg-primary-green +selected" : ""}`}>

{`${item.label}`}

	)
};

function nearest(element, className) {
	if (!element) return false;
	return element.className.indexOf(className) > -1 || nearest(element.parentElement, className);
}

const MultiSelector = React.createClass({

	componentWillMount: function() {
		document.addEventListener('click', this.state.close);
	},

	componentWillUnmount: function() {
		document.removeEventListener('click', this.state.close);
	},

	getInitialState: function() {
		return {
			selectedItems: this.props.initialSelectedItems || [],
			mouseIndex: null,
			mouseActive: true,
			mouseFunc: null,
			dialogDisplayed: false,
			activeIndex: null,
			searchValue: '',
			close: (e) => {
				if (!nearest(e.target, 'cp-multi-selector')) {
					this.setState({
						dialogDisplayed: false
					});
				}
			}
		}
	},

	componentWillReceiveProps(nextProps) {
		this.setState({
			selectedItems: nextProps.initialSelectedItems
		})
	},

	displayDialog: function(e) {
		this.setState({
			dialogDisplayed: true
		})
	},

	removeItem: function(item, e) {
		this.setState({
			selectedItems: without(this.state.selectedItems, item)
		});

		setTimeout(this.triggerItemChange);
	},

	getItemTitle: function(item) {
		return item.label;
	},
	setActiveIndex(index) {
		this.setState({
			activeIndex: index,
			mouseIndex: null,
		}, () => {
			this.searchItems[this.state.activeIndex].scrollIntoView();
			if (this.state.mouseActive) {
				this.setState({
					mouseActive: false,
					mouseFunc: () => {
						this.setState({
							mouseActive: true
						})
						document.removeEventListener("mousemove", this.state.mouseFunc);
					}
				}, () => {
					document.addEventListener("mousemove", this.state.mouseFunc);
				})
			}
		})
	},
	keyDown: function(e) {
		const keycode = e.which;
		const activeIndex = this.state.activeIndex;
		const filterItems = this.getFilterItems(this.props.items);
		this.props.onInputChange && this.props.onInputChange(e.currentTarget.value);
		if (keycode === 13) e.preventDefault();
		if(keycode === 40) { // press down key
			if(isNull(activeIndex)) {
				return this.setActiveIndex(0);
			} else {
				if(activeIndex < filterItems.length - 1) {
					return this.setActiveIndex(activeIndex + 1);
				}
			}
		} else if(keycode === 38) { // press up key
			if(!activeIndex) {
				return this.setActiveIndex(0);
			} else {
				if(activeIndex > 0) {
					return this.setActiveIndex(activeIndex - 1);
				}
			}
		} else if(keycode === 13) { // press enter key
			if(!isNull(activeIndex)) {
				return this.selectItem(filterItems[activeIndex]);
			} else if(this.props.noRestrict) {
				// if the noRestrict prop is true it adds the input as a string to the selected items on enter
				this.selectItem(e.currentTarget.value);
				e.currentTarget.value = "";
			}
		} else if(keycode === 27) { // press escape key
			return this.setState({
				activeIndex: null,
				dialogDisplayed: false
			});
		}

		this.setState({
			searchValue: e.target.value
		});
	},

	triggerItemChange: function() {
		if (this.props.onChange) {
			this.props.onChange.call(null, this.state.selectedItems);
		}
	},

	getSelectedClass: function(item) {
		return includes(this.state.selectedItems, item) ? '+selected' : '';
	},

	getActiveClass: function(index) {
		return this.state.activeIndex === index || this.state.mouseIndex === index
			? '+highlighted'
			: '';
	},

	getFilterItems: function(items = []) {
		let getItemTitle = this.props.getItemTitle || this.getItemTitle;

		return items
			.filter((item) => {
				return getItemTitle(item).toLowerCase().indexOf(this.state.searchValue.toLowerCase()) > -1;
			})
	},

	getSearchItems: function(items = []) {
		let ItemComponent = this.props.ItemComponent || DefaultItemComponent;

		return this.getFilterItems(items).map((item, index) => {
			return (

 {
						if (this.searchItems) {
							this.searchItems[index] = ref;
						} else {
							this.searchItems = [];
							this.searchItems[index] = ref;
						}
					}}
					onMouseOver={() => {
						if (this.state.mouseActive) {
							this.setState({
								mouseIndex: index
							})
						}
					}}
					className={`cp-multi-selector-item ${this.getSelectedClass(item)} ${this.getActiveClass(index)}`}
					onClick={this.selectItem.bind(this, item)}>
					<ItemComponent item={item} selectedItems={this.state.selectedItems}/>

			)
		})
	},

	selectItem: function(item, e) {
		let selectedItems = this.state.selectedItems;

		if(includes(selectedItems, item)) {
			this.setState({
				selectedItems: without(selectedItems, item)
			});
		} else {
			this.setState({
				selectedItems: union(selectedItems, [ item ])
			});
		}

		setTimeout(this.triggerItemChange);
	},

	positionDialog: function() {
		setTimeout(() => {
			let el = this.el;
			let height = el.clientHeight;
			let dialog = el.querySelector('.cp-multi-selector__dialog');

			dialog.style.top = (height + 1) + 'px';

			el.querySelector('.cp-multi-selector__dialog__input').focus();
		}, 100);
	},

	prevent: function(e){
		if(e.which === 13) e.preventDefault();
	},

	render: function() {
		//Get getItemTitle is the function that should be passed in to decide what `pill` will display on selection.
		let getItemTitle = this.props.getItemTitle || this.getItemTitle;

		let pills = this.state.selectedItems
			.map((item, i) => {
				return (
					<div key={i} className="cp-multi-selector__pill" title={`${getItemTitle(item)}`}>

							{getItemTitle(item)}

				);
			});

		let dialog;
		let that = this;

		if (this.state.dialogDisplayed) {
			let placeholder = this.props.placeholder ? this.props.placeholder : "Type a collaborators name...";
			dialog = (

						<input
							onKeyDown={this.keyDown}
							className="cps-form-control cp-multi-selector__dialog__input"
							placeholder={placeholder}/>

						{this.getSearchItems(this.props.items)}

			)

			this.positionDialog();
		}

		return (

 { if (el) that.el = el }} className='cp-multi-selector'>
				<input type="input" className="cp-multi-selector__hidden-input" onFocus={this.displayDialog}/>

					{pills}

				{dialog}

		)
	}
});

if (window && !window.MultiSelector) window.MultiSelector = MultiSelector;

export default MultiSelector;
<commit_msg>Fix bug in cpr-select<commit_after>import React from 'react';
import {without, includes, union, isNull} from 'lodash';

function DefaultItemComponent(props) {
	const item = props.item;
	const selectedItems = props.selectedItems;
	const selected = includes(selectedItems, item);

	return (
		<div title={`${item.label}`}>
			<div
				className={`cp-multi-selector-item__icon ${selected ? "cps-bg-primary-green +selected" : ""}`}>

{`${item.label}`}

	)
};

function nearest(element, className) {
	if (!element) return false;
	return element.className.indexOf(className) > -1 || nearest(element.parentElement, className);
}

const MultiSelector = React.createClass({

	componentWillMount: function() {
		document.addEventListener('click', this.state.close);
	},

	componentWillUnmount: function() {
		document.removeEventListener('click', this.state.close);
	},

	getInitialState: function() {
		return {
			selectedItems: this.props.initialSelectedItems || [],
			mouseIndex: null,
			mouseActive: true,
			mouseFunc: null,
			dialogDisplayed: false,
			activeIndex: null,
			searchValue: '',
			close: (e) => {
				if (!nearest(e.target, 'cp-multi-selector')) {
					this.setState({
						dialogDisplayed: false
					});
				}
			}
		}
	},

	componentWillReceiveProps(nextProps) {
		this.setState({
			selectedItems: nextProps.initialSelectedItems || []
		})
	},

	displayDialog: function(e) {
		this.setState({
			dialogDisplayed: true
		})
	},

	removeItem: function(item, e) {
		this.setState({
			selectedItems: without(this.state.selectedItems, item)
		});

		setTimeout(this.triggerItemChange);
	},

	getItemTitle: function(item) {
		return item.label;
	},
	setActiveIndex(index) {
		this.setState({
			activeIndex: index,
			mouseIndex: null,
		}, () => {
			this.searchItems[this.state.activeIndex].scrollIntoView();
			if (this.state.mouseActive) {
				this.setState({
					mouseActive: false,
					mouseFunc: () => {
						this.setState({
							mouseActive: true
						})
						document.removeEventListener("mousemove", this.state.mouseFunc);
					}
				}, () => {
					document.addEventListener("mousemove", this.state.mouseFunc);
				})
			}
		})
	},
	keyDown: function(e) {
		const keycode = e.which;
		const activeIndex = this.state.activeIndex;
		const filterItems = this.getFilterItems(this.props.items);
		this.props.onInputChange && this.props.onInputChange(e.currentTarget.value);
		if (keycode === 13) e.preventDefault();
		if(keycode === 40) { // press down key
			if(isNull(activeIndex)) {
				return this.setActiveIndex(0);
			} else {
				if(activeIndex < filterItems.length - 1) {
					return this.setActiveIndex(activeIndex + 1);
				}
			}
		} else if(keycode === 38) { // press up key
			if(!activeIndex) {
				return this.setActiveIndex(0);
			} else {
				if(activeIndex > 0) {
					return this.setActiveIndex(activeIndex - 1);
				}
			}
		} else if(keycode === 13) { // press enter key
			if(!isNull(activeIndex)) {
				return this.selectItem(filterItems[activeIndex]);
			} else if(this.props.noRestrict) {
				// if the noRestrict prop is true it adds the input as a string to the selected items on enter
				this.selectItem(e.currentTarget.value);
				e.currentTarget.value = "";
			}
		} else if(keycode === 27) { // press escape key
			return this.setState({
				activeIndex: null,
				dialogDisplayed: false
			});
		}

		this.setState({
			searchValue: e.target.value
		});
	},

	triggerItemChange: function() {
		if (this.props.onChange) {
			this.props.onChange.call(null, this.state.selectedItems);
		}
	},

	getSelectedClass: function(item) {
		return includes(this.state.selectedItems, item) ? '+selected' : '';
	},

	getActiveClass: function(index) {
		return this.state.activeIndex === index || this.state.mouseIndex === index
			? '+highlighted'
			: '';
	},

	getFilterItems: function(items = []) {
		let getItemTitle = this.props.getItemTitle || this.getItemTitle;

		return items
			.filter((item) => {
				return getItemTitle(item).toLowerCase().indexOf(this.state.searchValue.toLowerCase()) > -1;
			})
	},

	getSearchItems: function(items = []) {
		let ItemComponent = this.props.ItemComponent || DefaultItemComponent;

		return this.getFilterItems(items).map((item, index) => {
			return (

 {
						if (this.searchItems) {
							this.searchItems[index] = ref;
						} else {
							this.searchItems = [];
							this.searchItems[index] = ref;
						}
					}}
					onMouseOver={() => {
						if (this.state.mouseActive) {
							this.setState({
								mouseIndex: index
							})
						}
					}}
					className={`cp-multi-selector-item ${this.getSelectedClass(item)} ${this.getActiveClass(index)}`}
					onClick={this.selectItem.bind(this, item)}>
					<ItemComponent item={item} selectedItems={this.state.selectedItems}/>

			)
		})
	},

	selectItem: function(item, e) {
		let selectedItems = this.state.selectedItems;

		if(includes(selectedItems, item)) {
			this.setState({
				selectedItems: without(selectedItems, item)
			});
		} else {
			this.setState({
				selectedItems: union(selectedItems, [ item ])
			});
		}

		setTimeout(this.triggerItemChange);
	},

	positionDialog: function() {
		setTimeout(() => {
			let el = this.el;
			let height = el.clientHeight;
			let dialog = el.querySelector('.cp-multi-selector__dialog');

			dialog.style.top = (height + 1) + 'px';

			el.querySelector('.cp-multi-selector__dialog__input').focus();
		}, 100);
	},

	prevent: function(e){
		if(e.which === 13) e.preventDefault();
	},

	render: function() {
		//Get getItemTitle is the function that should be passed in to decide what `pill` will display on selection.
		let getItemTitle = this.props.getItemTitle || this.getItemTitle;

		let pills = this.state.selectedItems
			.map((item, i) => {
				return (
					<div key={i} className="cp-multi-selector__pill" title={`${getItemTitle(item)}`}>

							{getItemTitle(item)}

				);
			});

		let dialog;
		let that = this;

		if (this.state.dialogDisplayed) {
			let placeholder = this.props.placeholder ? this.props.placeholder : "Type a collaborators name...";
			dialog = (

						<input
							onKeyDown={this.keyDown}
							className="cps-form-control cp-multi-selector__dialog__input"
							placeholder={placeholder}/>

						{this.getSearchItems(this.props.items)}

			)

			this.positionDialog();
		}

		return (

 { if (el) that.el = el }} className='cp-multi-selector'>
				<input type="input" className="cp-multi-selector__hidden-input" onFocus={this.displayDialog}/>

					{pills}

				{dialog}

		)
	}
});

if (window && !window.MultiSelector) window.MultiSelector = MultiSelector;

export default MultiSelector;
<|endoftext|>


<commit_before>var events = require('events'),
  util = require('util'),
  mysql = require('mysql'),
  _ = require('lodash'),
  defaultCallback = require('../utils/defaultCallback'),
  Collection = require('./Collection');

/**
 * Constructs a new MySQL database.
 * @param {Object} connectionProperties connection properties.
 * @see https://github.com/felixge/node-mysql#connection-options for a list of connection properties to use.
 * @constructor
 */
function Database(connectionProperties) {
  this.connectionProperties = connectionProperties;
  this.tables = {};
  this.isConnected = false;

  events.EventEmitter.call(this);
  this.setMaxListeners(99);
}

// Database extends the EventEmitter class
util.inherits(Database, events.EventEmitter);

/**
 * Attempts to connect to database, using the connection properties given at construction time.
 * @param {Function} [callback] a callback function to execute when connection has been established, i.e. function (err).
 * @returns {Database} this instance, to enable method chaining.
 */
Database.prototype.connect = function (callback) {
  // handle optional "callback" param
  if (typeof callback !== 'function') {
    callback = defaultCallback;
  }

  if (!this.isConnected) {
    this._pool = mysql.createPool(this.connectionProperties);
    this.isConnected = true;

    this.emit('connect');
  }

  callback();

  return this;
};

/**
 * Gracefully closes all database connections.
 * The instance will become practically useless after calling this method, unless calling connect() again.
 * @param {Function} [callback] a callback function to execute when connection has been closed, i.e. function (err).
 * @returns {Database} this to enable method chaining.
 */
Database.prototype.disconnect = function (callback) {
  // handle optional "callback" param
  if (typeof callback !== 'function') {
    callback = defaultCallback;
  }

  if (this.isConnected) {
    this._pool.end(callback);
    this.isConnected = false;

    this.emit('disconnect');

  } else {
    callback();
  }

  return this;
};

/**
 * Runs the given SQL statement to the database.
 * @param {String} sql a parameterized SQL statement.
 * @param {Array} [params] an array of parameter values.
 * @param {Object} [options] query options, i.e. {nestTables: true} to handle overlapping column names.
 * @param {Function} [callback] a callback function, i.e. function(error, records, meta) for SELECT statements and function(error, meta) for DML statements.
 */
Database.prototype.query = function (sql, params, options, callback) {
  var type;

  // make sure "sql" parameter is valid
  type = typeof(sql);

  if (type !== 'string') {
    throw new Error('You must specify a valid SQL statement');
  }

  // make sure "params" parameter is valid
  if (!_.isArray(params)) {
    type = typeof(params);

    if (_.isPlainObject(params)) {
      options = params;
    } else if (type === 'function') {
      callback = params;
    } else if (type !== 'undefined') { // not Array, nor Object, nor Function, nor undefined
      throw new Error('Invalid query parameters - expected array, received ' + type);
    }

    params = [];
  }

  // make sure "options" parameter is valid
  if (!_.isPlainObject(options)) {
    type = typeof(options);

    if (type === 'function') {
      callback = options;
    } else if (type !== 'undefined') { // not Object, nor Function, nor undefined
      throw new Error('Invalid query options - expected object, received ' + type);
    }

    options = {};
  }

  // make sure "callback" parameter is valid
  type = typeof(callback);

  if (type !== 'function') {
    if (type === 'undefined') {
      callback = defaultCallback;
    } else {
      throw new Error('Invalid callback - expected function, received ' + type);
    }
  }

  // check if db connected
  if (!this.isConnected) {
    return callback(new Error('Connection is closed - did you forget to call #connect()?'));
  }

  // get an available db connection
  this._pool.getConnection(function (error, connection) {
    if (error) return callback(error);

    // use the "nestTables" option Luke
    if (options.nestTables) {
      sql = {
        sql: sql,
        nestTables: options.nestTables
      };
    }

    // run Forrest, run
    connection.query(sql, params, function(error, records) {
      var meta = {};

      if (error) {
        callback(error);

      } else if (Array.isArray(records)) { // SELECT statement
        callback(null, records);

      } else { // DML statement
        meta.insertId = records.insertId;
        meta.affectedRows = records.affectedRows;

        callback(null, meta);
      }

      connection.release();
    });
  });
};

/**
 * Creates and returns a new data collection representing the designated table.
 * @param {String} table the name of an existing table in database.
 * @param {Object} [customProperties] the collection's custom properties.
 * Please note that this function will not create a new table on database.
 */
Database.prototype.extend = function (table, customProperties) {
  var collection;

  // make sure "table" parameter is valid
  if (typeof table !== 'string') {
    throw new Error('Invalid table name');
  }

  // create new collection
  collection = new Collection(this, table);

  // assign custom properties
  if (_.isPlainObject(customProperties)) {
    collection = _.extend(collection, customProperties);
  }

  return collection;
};

module.exports = Database;
<commit_msg>Improving the error message on #extend() with invalid table param.<commit_after>var events = require('events'),
  util = require('util'),
  mysql = require('mysql'),
  _ = require('lodash'),
  defaultCallback = require('../utils/defaultCallback'),
  Collection = require('./Collection');

/**
 * Constructs a new MySQL database.
 * @param {Object} connectionProperties connection properties.
 * @see https://github.com/felixge/node-mysql#connection-options for a list of connection properties to use.
 * @constructor
 */
function Database(connectionProperties) {
  this.connectionProperties = connectionProperties;
  this.tables = {};
  this.isConnected = false;

  events.EventEmitter.call(this);
  this.setMaxListeners(99);
}

// Database extends the EventEmitter class
util.inherits(Database, events.EventEmitter);

/**
 * Attempts to connect to database, using the connection properties given at construction time.
 * @param {Function} [callback] a callback function to execute when connection has been established, i.e. function (err).
 * @returns {Database} this instance, to enable method chaining.
 */
Database.prototype.connect = function (callback) {
  // handle optional "callback" param
  if (typeof callback !== 'function') {
    callback = defaultCallback;
  }

  if (!this.isConnected) {
    this._pool = mysql.createPool(this.connectionProperties);
    this.isConnected = true;

    this.emit('connect');
  }

  callback();

  return this;
};

/**
 * Gracefully closes all database connections.
 * The instance will become practically useless after calling this method, unless calling connect() again.
 * @param {Function} [callback] a callback function to execute when connection has been closed, i.e. function (err).
 * @returns {Database} this to enable method chaining.
 */
Database.prototype.disconnect = function (callback) {
  // handle optional "callback" param
  if (typeof callback !== 'function') {
    callback = defaultCallback;
  }

  if (this.isConnected) {
    this._pool.end(callback);
    this.isConnected = false;

    this.emit('disconnect');

  } else {
    callback();
  }

  return this;
};

/**
 * Runs the given SQL statement to the database.
 * @param {String} sql a parameterized SQL statement.
 * @param {Array} [params] an array of parameter values.
 * @param {Object} [options] query options, i.e. {nestTables: true} to handle overlapping column names.
 * @param {Function} [callback] a callback function, i.e. function(error, records, meta) for SELECT statements and function(error, meta) for DML statements.
 */
Database.prototype.query = function (sql, params, options, callback) {
  var type;

  // make sure "sql" parameter is valid
  type = typeof(sql);

  if (type !== 'string') {
    throw new Error('You must specify a valid SQL statement');
  }

  // make sure "params" parameter is valid
  if (!_.isArray(params)) {
    type = typeof(params);

    if (_.isPlainObject(params)) {
      options = params;
    } else if (type === 'function') {
      callback = params;
    } else if (type !== 'undefined') { // not Array, nor Object, nor Function, nor undefined
      throw new Error('Invalid query parameters - expected array, received ' + type);
    }

    params = [];
  }

  // make sure "options" parameter is valid
  if (!_.isPlainObject(options)) {
    type = typeof(options);

    if (type === 'function') {
      callback = options;
    } else if (type !== 'undefined') { // not Object, nor Function, nor undefined
      throw new Error('Invalid query options - expected object, received ' + type);
    }

    options = {};
  }

  // make sure "callback" parameter is valid
  type = typeof(callback);

  if (type !== 'function') {
    if (type === 'undefined') {
      callback = defaultCallback;
    } else {
      throw new Error('Invalid callback - expected function, received ' + type);
    }
  }

  // check if db connected
  if (!this.isConnected) {
    return callback(new Error('Connection is closed - did you forget to call #connect()?'));
  }

  // get an available db connection
  this._pool.getConnection(function (error, connection) {
    if (error) return callback(error);

    // use the "nestTables" option Luke
    if (options.nestTables) {
      sql = {
        sql: sql,
        nestTables: options.nestTables
      };
    }

    // run Forrest, run
    connection.query(sql, params, function(error, records) {
      var meta = {};

      if (error) {
        callback(error);

      } else if (Array.isArray(records)) { // SELECT statement
        callback(null, records);

      } else { // DML statement
        meta.insertId = records.insertId;
        meta.affectedRows = records.affectedRows;

        callback(null, meta);
      }

      connection.release();
    });
  });
};

/**
 * Creates and returns a new data collection representing the designated table.
 * @param {String} table the name of an existing table in database.
 * @param {Object} [customProperties] the collection's custom properties.
 * Please note that this function will not create a new table on database.
 */
Database.prototype.extend = function (table, customProperties) {
  var collection;

  // make sure "table" parameter is valid
  if (typeof table !== 'string') {
    throw new Error('Invalid table name - expected string, received ' + typeof(table));
  }

  // create new collection
  collection = new Collection(this, table);

  // assign custom properties
  if (_.isPlainObject(customProperties)) {
    collection = _.extend(collection, customProperties);
  }

  return collection;
};

module.exports = Database;
<|endoftext|>


<commit_before>import $ from 'jquery';
import ParsleyUtils from './utils';

var ParsleyUI = function (options) {
  this.__class__ = 'ParsleyUI';
};

ParsleyUI.prototype = {
  listen: function () {
    window.Parsley
    .on('form:init',       (form ) => { this.setupForm (form ); } )
    .on('field:init',      (field) => { this.setupField(field); } )
    .on('field:validated', (field) => { this.reflow    (field); } )
    .on('form:validated',  (form ) => { this.focus     (form ); } )
    .on('field:reset',     (field) => { this.reset     (field); } )
    .on('form:destroy',    (form ) => { this.destroy   (form ); } )
    .on('field:destroy',   (field) => { this.destroy   (field); } );

    return this;
  },

  reflow: function (fieldInstance) {
    // If this field has not an active UI (case for multiples) don't bother doing something
    if ('undefined' === typeof fieldInstance._ui || false === fieldInstance._ui.active)
      return;

    // Diff between two validation results
    var diff = this._diff(fieldInstance.validationResult, fieldInstance._ui.lastValidationResult);

    // Then store current validation result for next reflow
    fieldInstance._ui.lastValidationResult = fieldInstance.validationResult;

    // Field have been validated at least once if here. Useful for binded key events...
    fieldInstance._ui.validatedOnce = true;

    // Handle valid / invalid / none field class
    this.manageStatusClass(fieldInstance);

    // Add, remove, updated errors messages
    this.manageErrorsMessages(fieldInstance, diff);

    // Triggers impl
    this.actualizeTriggers(fieldInstance);

    // If field is not valid for the first time, bind keyup trigger to ease UX and quickly inform user
    if ((diff.kept.length || diff.added.length) && true !== fieldInstance._ui.failedOnce)
      this.manageFailingFieldTrigger(fieldInstance);
  },

  // Returns an array of field's error message(s)
  getErrorsMessages: function (fieldInstance) {
    // No error message, field is valid
    if (true === fieldInstance.validationResult)
      return [];

    var messages = [];

    for (var i = 0; i < fieldInstance.validationResult.length; i++)
      messages.push(fieldInstance.validationResult[i].errorMessage ||
       this._getErrorMessage(fieldInstance, fieldInstance.validationResult[i].assert));

    return messages;
  },

  manageStatusClass: function (fieldInstance) {
    if (fieldInstance.hasConstraints() && fieldInstance.needsValidation() && true === fieldInstance.validationResult)
      this._successClass(fieldInstance);
    else if (fieldInstance.validationResult.length > 0)
      this._errorClass(fieldInstance);
    else
      this._resetClass(fieldInstance);
  },

  manageErrorsMessages: function (fieldInstance, diff) {
    if ('undefined' !== typeof fieldInstance.options.errorsMessagesDisabled)
      return;

    // Case where we have errorMessage option that configure an unique field error message, regardless failing validators
    if ('undefined' !== typeof fieldInstance.options.errorMessage) {
      if ((diff.added.length || diff.kept.length)) {
        this._insertErrorWrapper(fieldInstance);

        if (0 === fieldInstance._ui.$errorsWrapper.find('.parsley-custom-error-message').length)
          fieldInstance._ui.$errorsWrapper
            .append(
              $(fieldInstance.options.errorTemplate)
              .addClass('parsley-custom-error-message')
            );

        return fieldInstance._ui.$errorsWrapper
          .addClass('filled')
          .find('.parsley-custom-error-message')
          .html(fieldInstance.options.errorMessage);
      }

      return fieldInstance._ui.$errorsWrapper
        .removeClass('filled')
        .find('.parsley-custom-error-message')
        .remove();
    }

    // Show, hide, update failing constraints messages
    for (var i = 0; i < diff.removed.length; i++)
      this.removeError(fieldInstance, diff.removed[i].assert.name, true);

    for (i = 0; i < diff.added.length; i++)
      this.addError(fieldInstance, diff.added[i].assert.name, diff.added[i].errorMessage, diff.added[i].assert, true);

    for (i = 0; i < diff.kept.length; i++)
      this.updateError(fieldInstance, diff.kept[i].assert.name, diff.kept[i].errorMessage, diff.kept[i].assert, true);
  },

  // TODO: strange API here, intuitive for manual usage with addError(pslyInstance, 'foo', 'bar')
  // but a little bit complex for above internal usage, with forced undefined parameter...
  addError: function (fieldInstance, name, message, assert, doNotUpdateClass) {
    this._insertErrorWrapper(fieldInstance);
    fieldInstance._ui.$errorsWrapper
      .addClass('filled')
      .append(
        $(fieldInstance.options.errorTemplate)
        .addClass('parsley-' + name)
        .html(message || this._getErrorMessage(fieldInstance, assert))
      );

    if (true !== doNotUpdateClass)
      this._errorClass(fieldInstance);
  },

  // Same as above
  updateError: function (fieldInstance, name, message, assert, doNotUpdateClass) {
    fieldInstance._ui.$errorsWrapper
      .addClass('filled')
      .find('.parsley-' + name)
      .html(message || this._getErrorMessage(fieldInstance, assert));

    if (true !== doNotUpdateClass)
      this._errorClass(fieldInstance);
  },

  // Same as above twice
  removeError: function (fieldInstance, name, doNotUpdateClass) {
    fieldInstance._ui.$errorsWrapper
      .removeClass('filled')
      .find('.parsley-' + name)
      .remove();

    // edge case possible here: remove a standard Parsley error that is still failing in fieldInstance.validationResult
    // but highly improbable cuz' manually removing a well Parsley handled error makes no sense.
    if (true !== doNotUpdateClass)
      this.manageStatusClass(fieldInstance);
  },

  focus: function (formInstance) {
    formInstance._focusedField = null;

    if (true === formInstance.validationResult || 'none' === formInstance.options.focus)
      return null;

    for (var i = 0; i < formInstance.fields.length; i++) {
      var field = formInstance.fields[i];
      if (true !== field.validationResult && field.validationResult.length > 0 && 'undefined' === typeof field.options.noFocus) {
        formInstance._focusedField = field.$element;
        if ('first' === formInstance.options.focus)
          break;
      }
    }

    if (null === formInstance._focusedField)
      return null;

    return formInstance._focusedField.focus();
  },

  _getErrorMessage: function (fieldInstance, constraint) {
    var customConstraintErrorMessage = constraint.name + 'Message';

    if ('undefined' !== typeof fieldInstance.options[customConstraintErrorMessage])
      return window.Parsley.formatMessage(fieldInstance.options[customConstraintErrorMessage], constraint.requirements);

    return window.Parsley.getErrorMessage(constraint);
  },

  _diff: function (newResult, oldResult, deep) {
    var added = [];
    var kept = [];

    for (var i = 0; i < newResult.length; i++) {
      var found = false;

      for (var j = 0; j < oldResult.length; j++)
        if (newResult[i].assert.name === oldResult[j].assert.name) {
          found = true;
          break;
        }

      if (found)
        kept.push(newResult[i]);
      else
        added.push(newResult[i]);
    }

    return {
      kept: kept,
      added: added,
      removed: !deep ? this._diff(oldResult, newResult, true).added : []
    };
  },

  setupForm: function (formInstance) {
    formInstance.$element.on('submit.Parsley', false, evt => { formInstance.onSubmitValidate(evt); });
    formInstance.$element.on('click.Parsley', 'input[type="submit"], button[type="submit"]', evt => { formInstance.onSubmitButton(evt); });

    // UI could be disabled
    if (false === formInstance.options.uiEnabled)
      return;

    formInstance.$element.attr('novalidate', '');
  },

  setupField: function (fieldInstance) {
    var _ui = {active: false};

    // UI could be disabled
    if (false === fieldInstance.options.uiEnabled)
      return;

    _ui.active = true;

    // Give field its Parsley id in DOM
    fieldInstance.$element.attr(fieldInstance.options.namespace + 'id', fieldInstance.__id__);

    /** Generate important UI elements and store them in fieldInstance **/
    // $errorClassHandler is the $element that woul have parsley-error and parsley-success classes
    _ui.$errorClassHandler = this._manageClassHandler(fieldInstance);

    // $errorsWrapper is a div that would contain the various field errors, it will be appended into $errorsContainer
    _ui.errorsWrapperId = 'parsley-id-' + (fieldInstance.options.multiple ? 'multiple-' + fieldInstance.options.multiple : fieldInstance.__id__);
    _ui.$errorsWrapper = $(fieldInstance.options.errorsWrapper).attr('id', _ui.errorsWrapperId);

    // ValidationResult UI storage to detect what have changed bwt two validations, and update DOM accordingly
    _ui.lastValidationResult = [];
    _ui.validatedOnce = false;
    _ui.validationInformationVisible = false;

    // Store it in fieldInstance for later
    fieldInstance._ui = _ui;

    // Bind triggers first time
    this.actualizeTriggers(fieldInstance);
  },

  // Determine which element will have `parsley-error` and `parsley-success` classes
  _manageClassHandler: function (fieldInstance) {
    // An element selector could be passed through DOM with `data-parsley-class-handler=#foo`
    if ('string' === typeof fieldInstance.options.classHandler && $(fieldInstance.options.classHandler).length)
      return $(fieldInstance.options.classHandler);

    // Class handled could also be determined by function given in Parsley options
    var $handler = fieldInstance.options.classHandler(fieldInstance);

    // If this function returned a valid existing DOM element, go for it
    if ('undefined' !== typeof $handler && $handler.length)
      return $handler;

    // Otherwise, if simple element (input, texatrea, select...) it will perfectly host the classes
    if (!fieldInstance.options.multiple || fieldInstance.$element.is('select'))
      return fieldInstance.$element;

    // But if multiple element (radio, checkbox), that would be their parent
    return fieldInstance.$element.parent();
  },

  _insertErrorWrapper: function (fieldInstance) {
    var $errorsContainer;

    // Nothing to do if already inserted
    if (0 !== fieldInstance._ui.$errorsWrapper.parent().length)
      return fieldInstance._ui.$errorsWrapper.parent();

    if ('string' === typeof fieldInstance.options.errorsContainer) {
      if ($(fieldInstance.options.errorsContainer).length)
        return $(fieldInstance.options.errorsContainer).append(fieldInstance._ui.$errorsWrapper);
      else
        ParsleyUtils.warn('The errors container `' + fieldInstance.options.errorsContainer + '` does not exist in DOM');
    } else if ('function' === typeof fieldInstance.options.errorsContainer)
      $errorsContainer = fieldInstance.options.errorsContainer(fieldInstance);

    if ('undefined' !== typeof $errorsContainer && $errorsContainer.length)
      return $errorsContainer.append(fieldInstance._ui.$errorsWrapper);

    var $from = fieldInstance.$element;
    if (fieldInstance.options.multiple)
      $from = $from.parent();
    return $from.after(fieldInstance._ui.$errorsWrapper);
  },

  actualizeTriggers: function (fieldInstance) {
    var $toBind = fieldInstance.$element;
    if (fieldInstance.options.multiple)
      $toBind = $('[' + fieldInstance.options.namespace + 'multiple="' + fieldInstance.options.multiple + '"]');

    // Remove Parsley events already binded on this field
    $toBind.off('.Parsley');

    // If no trigger is set, all good
    if (false === fieldInstance.options.trigger)
      return;

    var triggers = fieldInstance.options.trigger.replace(/^\s+/g , '').replace(/\s+$/g , '');

    if ('' === triggers)
      return;

    // Bind fieldInstance.eventValidate if exists (for parsley.ajax for example), ParsleyUI.eventValidate otherwise
    $toBind.on(
      triggers.split(' ').join('.Parsley ') + '.Parsley',
      $.proxy('function' === typeof fieldInstance.eventValidate ? fieldInstance.eventValidate : this.eventValidate, fieldInstance));
  },

  // Called through $.proxy with fieldInstance. `this` context is ParsleyField
  eventValidate: function (event) {
    // For keyup, keypress, keydown... events that could be a little bit obstrusive
    // do not validate if val length < min threshold on first validation. Once field have been validated once and info
    // about success or failure have been displayed, always validate with this trigger to reflect every yalidation change.
    if (/key/.test(event.type))
      if (!this._ui.validationInformationVisible && this.getValue().length <= this.options.validationThreshold)
        return;

    this._ui.validatedOnce = true;
    this.validate();
  },

  manageFailingFieldTrigger: function (fieldInstance) {
    fieldInstance._ui.failedOnce = true;

    // Radio and checkboxes fields must bind every field multiple
    if (fieldInstance.options.multiple)
      $('[' + fieldInstance.options.namespace + 'multiple="' + fieldInstance.options.multiple + '"]').each(function () {
        if (!/change/i.test($(this).parsley().options.trigger || ''))
          return $(this).on('change.ParsleyFailedOnce', false, () => { fieldInstance.validate(); });
      });

    // Select case
    if (fieldInstance.$element.is('select'))
      if (!/change/i.test(fieldInstance.options.trigger || ''))
        return fieldInstance.$element.on('change.ParsleyFailedOnce', false, () => { fieldInstance.validate(); });

    // All other inputs fields
    if (!/keyup/i.test(fieldInstance.options.trigger || ''))
      return fieldInstance.$element.on('keyup.ParsleyFailedOnce', false, () => { fieldInstance.validate(); });
  },

  reset: function (parsleyInstance) {
    // Reset all event listeners
    this.actualizeTriggers(parsleyInstance);
    parsleyInstance.$element.off('.ParsleyFailedOnce');

    // Nothing to do if UI never initialized for this field
    if ('undefined' === typeof parsleyInstance._ui)
      return;

    if ('ParsleyForm' === parsleyInstance.__class__)
      return;

    // Reset all errors' li
    parsleyInstance._ui.$errorsWrapper
      .removeClass('filled')
      .children()
      .remove();

    // Reset validation class
    this._resetClass(parsleyInstance);

    // Reset validation flags and last validation result
    parsleyInstance._ui.validatedOnce = false;
    parsleyInstance._ui.lastValidationResult = [];
    parsleyInstance._ui.validationInformationVisible = false;
    parsleyInstance._ui.failedOnce = false;
  },

  destroy: function (parsleyInstance) {
    this.reset(parsleyInstance);

    if ('ParsleyForm' === parsleyInstance.__class__)
      return;

    if ('undefined' !== typeof parsleyInstance._ui)
      parsleyInstance._ui.$errorsWrapper.remove();

    delete parsleyInstance._ui;
  },

  _successClass: function (fieldInstance) {
    fieldInstance._ui.validationInformationVisible = true;
    fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.errorClass).addClass(fieldInstance.options.successClass);
  },
  _errorClass: function (fieldInstance) {
    fieldInstance._ui.validationInformationVisible = true;
    fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.successClass).addClass(fieldInstance.options.errorClass);
  },
  _resetClass: function (fieldInstance) {
    fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.successClass).removeClass(fieldInstance.options.errorClass);
  }
};

export default ParsleyUI;
<commit_msg>Remove useless 'return'<commit_after>import $ from 'jquery';
import ParsleyUtils from './utils';

var ParsleyUI = function (options) {
  this.__class__ = 'ParsleyUI';
};

ParsleyUI.prototype = {
  listen: function () {
    window.Parsley
    .on('form:init',       (form ) => { this.setupForm (form ); } )
    .on('field:init',      (field) => { this.setupField(field); } )
    .on('field:validated', (field) => { this.reflow    (field); } )
    .on('form:validated',  (form ) => { this.focus     (form ); } )
    .on('field:reset',     (field) => { this.reset     (field); } )
    .on('form:destroy',    (form ) => { this.destroy   (form ); } )
    .on('field:destroy',   (field) => { this.destroy   (field); } );

    return this;
  },

  reflow: function (fieldInstance) {
    // If this field has not an active UI (case for multiples) don't bother doing something
    if ('undefined' === typeof fieldInstance._ui || false === fieldInstance._ui.active)
      return;

    // Diff between two validation results
    var diff = this._diff(fieldInstance.validationResult, fieldInstance._ui.lastValidationResult);

    // Then store current validation result for next reflow
    fieldInstance._ui.lastValidationResult = fieldInstance.validationResult;

    // Field have been validated at least once if here. Useful for binded key events...
    fieldInstance._ui.validatedOnce = true;

    // Handle valid / invalid / none field class
    this.manageStatusClass(fieldInstance);

    // Add, remove, updated errors messages
    this.manageErrorsMessages(fieldInstance, diff);

    // Triggers impl
    this.actualizeTriggers(fieldInstance);

    // If field is not valid for the first time, bind keyup trigger to ease UX and quickly inform user
    if ((diff.kept.length || diff.added.length) && true !== fieldInstance._ui.failedOnce)
      this.manageFailingFieldTrigger(fieldInstance);
  },

  // Returns an array of field's error message(s)
  getErrorsMessages: function (fieldInstance) {
    // No error message, field is valid
    if (true === fieldInstance.validationResult)
      return [];

    var messages = [];

    for (var i = 0; i < fieldInstance.validationResult.length; i++)
      messages.push(fieldInstance.validationResult[i].errorMessage ||
       this._getErrorMessage(fieldInstance, fieldInstance.validationResult[i].assert));

    return messages;
  },

  manageStatusClass: function (fieldInstance) {
    if (fieldInstance.hasConstraints() && fieldInstance.needsValidation() && true === fieldInstance.validationResult)
      this._successClass(fieldInstance);
    else if (fieldInstance.validationResult.length > 0)
      this._errorClass(fieldInstance);
    else
      this._resetClass(fieldInstance);
  },

  manageErrorsMessages: function (fieldInstance, diff) {
    if ('undefined' !== typeof fieldInstance.options.errorsMessagesDisabled)
      return;

    // Case where we have errorMessage option that configure an unique field error message, regardless failing validators
    if ('undefined' !== typeof fieldInstance.options.errorMessage) {
      if ((diff.added.length || diff.kept.length)) {
        this._insertErrorWrapper(fieldInstance);

        if (0 === fieldInstance._ui.$errorsWrapper.find('.parsley-custom-error-message').length)
          fieldInstance._ui.$errorsWrapper
            .append(
              $(fieldInstance.options.errorTemplate)
              .addClass('parsley-custom-error-message')
            );

        return fieldInstance._ui.$errorsWrapper
          .addClass('filled')
          .find('.parsley-custom-error-message')
          .html(fieldInstance.options.errorMessage);
      }

      return fieldInstance._ui.$errorsWrapper
        .removeClass('filled')
        .find('.parsley-custom-error-message')
        .remove();
    }

    // Show, hide, update failing constraints messages
    for (var i = 0; i < diff.removed.length; i++)
      this.removeError(fieldInstance, diff.removed[i].assert.name, true);

    for (i = 0; i < diff.added.length; i++)
      this.addError(fieldInstance, diff.added[i].assert.name, diff.added[i].errorMessage, diff.added[i].assert, true);

    for (i = 0; i < diff.kept.length; i++)
      this.updateError(fieldInstance, diff.kept[i].assert.name, diff.kept[i].errorMessage, diff.kept[i].assert, true);
  },

  // TODO: strange API here, intuitive for manual usage with addError(pslyInstance, 'foo', 'bar')
  // but a little bit complex for above internal usage, with forced undefined parameter...
  addError: function (fieldInstance, name, message, assert, doNotUpdateClass) {
    this._insertErrorWrapper(fieldInstance);
    fieldInstance._ui.$errorsWrapper
      .addClass('filled')
      .append(
        $(fieldInstance.options.errorTemplate)
        .addClass('parsley-' + name)
        .html(message || this._getErrorMessage(fieldInstance, assert))
      );

    if (true !== doNotUpdateClass)
      this._errorClass(fieldInstance);
  },

  // Same as above
  updateError: function (fieldInstance, name, message, assert, doNotUpdateClass) {
    fieldInstance._ui.$errorsWrapper
      .addClass('filled')
      .find('.parsley-' + name)
      .html(message || this._getErrorMessage(fieldInstance, assert));

    if (true !== doNotUpdateClass)
      this._errorClass(fieldInstance);
  },

  // Same as above twice
  removeError: function (fieldInstance, name, doNotUpdateClass) {
    fieldInstance._ui.$errorsWrapper
      .removeClass('filled')
      .find('.parsley-' + name)
      .remove();

    // edge case possible here: remove a standard Parsley error that is still failing in fieldInstance.validationResult
    // but highly improbable cuz' manually removing a well Parsley handled error makes no sense.
    if (true !== doNotUpdateClass)
      this.manageStatusClass(fieldInstance);
  },

  focus: function (formInstance) {
    formInstance._focusedField = null;

    if (true === formInstance.validationResult || 'none' === formInstance.options.focus)
      return null;

    for (var i = 0; i < formInstance.fields.length; i++) {
      var field = formInstance.fields[i];
      if (true !== field.validationResult && field.validationResult.length > 0 && 'undefined' === typeof field.options.noFocus) {
        formInstance._focusedField = field.$element;
        if ('first' === formInstance.options.focus)
          break;
      }
    }

    if (null === formInstance._focusedField)
      return null;

    return formInstance._focusedField.focus();
  },

  _getErrorMessage: function (fieldInstance, constraint) {
    var customConstraintErrorMessage = constraint.name + 'Message';

    if ('undefined' !== typeof fieldInstance.options[customConstraintErrorMessage])
      return window.Parsley.formatMessage(fieldInstance.options[customConstraintErrorMessage], constraint.requirements);

    return window.Parsley.getErrorMessage(constraint);
  },

  _diff: function (newResult, oldResult, deep) {
    var added = [];
    var kept = [];

    for (var i = 0; i < newResult.length; i++) {
      var found = false;

      for (var j = 0; j < oldResult.length; j++)
        if (newResult[i].assert.name === oldResult[j].assert.name) {
          found = true;
          break;
        }

      if (found)
        kept.push(newResult[i]);
      else
        added.push(newResult[i]);
    }

    return {
      kept: kept,
      added: added,
      removed: !deep ? this._diff(oldResult, newResult, true).added : []
    };
  },

  setupForm: function (formInstance) {
    formInstance.$element.on('submit.Parsley', false, evt => { formInstance.onSubmitValidate(evt); });
    formInstance.$element.on('click.Parsley', 'input[type="submit"], button[type="submit"]', evt => { formInstance.onSubmitButton(evt); });

    // UI could be disabled
    if (false === formInstance.options.uiEnabled)
      return;

    formInstance.$element.attr('novalidate', '');
  },

  setupField: function (fieldInstance) {
    var _ui = {active: false};

    // UI could be disabled
    if (false === fieldInstance.options.uiEnabled)
      return;

    _ui.active = true;

    // Give field its Parsley id in DOM
    fieldInstance.$element.attr(fieldInstance.options.namespace + 'id', fieldInstance.__id__);

    /** Generate important UI elements and store them in fieldInstance **/
    // $errorClassHandler is the $element that woul have parsley-error and parsley-success classes
    _ui.$errorClassHandler = this._manageClassHandler(fieldInstance);

    // $errorsWrapper is a div that would contain the various field errors, it will be appended into $errorsContainer
    _ui.errorsWrapperId = 'parsley-id-' + (fieldInstance.options.multiple ? 'multiple-' + fieldInstance.options.multiple : fieldInstance.__id__);
    _ui.$errorsWrapper = $(fieldInstance.options.errorsWrapper).attr('id', _ui.errorsWrapperId);

    // ValidationResult UI storage to detect what have changed bwt two validations, and update DOM accordingly
    _ui.lastValidationResult = [];
    _ui.validatedOnce = false;
    _ui.validationInformationVisible = false;

    // Store it in fieldInstance for later
    fieldInstance._ui = _ui;

    // Bind triggers first time
    this.actualizeTriggers(fieldInstance);
  },

  // Determine which element will have `parsley-error` and `parsley-success` classes
  _manageClassHandler: function (fieldInstance) {
    // An element selector could be passed through DOM with `data-parsley-class-handler=#foo`
    if ('string' === typeof fieldInstance.options.classHandler && $(fieldInstance.options.classHandler).length)
      return $(fieldInstance.options.classHandler);

    // Class handled could also be determined by function given in Parsley options
    var $handler = fieldInstance.options.classHandler(fieldInstance);

    // If this function returned a valid existing DOM element, go for it
    if ('undefined' !== typeof $handler && $handler.length)
      return $handler;

    // Otherwise, if simple element (input, texatrea, select...) it will perfectly host the classes
    if (!fieldInstance.options.multiple || fieldInstance.$element.is('select'))
      return fieldInstance.$element;

    // But if multiple element (radio, checkbox), that would be their parent
    return fieldInstance.$element.parent();
  },

  _insertErrorWrapper: function (fieldInstance) {
    var $errorsContainer;

    // Nothing to do if already inserted
    if (0 !== fieldInstance._ui.$errorsWrapper.parent().length)
      return fieldInstance._ui.$errorsWrapper.parent();

    if ('string' === typeof fieldInstance.options.errorsContainer) {
      if ($(fieldInstance.options.errorsContainer).length)
        return $(fieldInstance.options.errorsContainer).append(fieldInstance._ui.$errorsWrapper);
      else
        ParsleyUtils.warn('The errors container `' + fieldInstance.options.errorsContainer + '` does not exist in DOM');
    } else if ('function' === typeof fieldInstance.options.errorsContainer)
      $errorsContainer = fieldInstance.options.errorsContainer(fieldInstance);

    if ('undefined' !== typeof $errorsContainer && $errorsContainer.length)
      return $errorsContainer.append(fieldInstance._ui.$errorsWrapper);

    var $from = fieldInstance.$element;
    if (fieldInstance.options.multiple)
      $from = $from.parent();
    return $from.after(fieldInstance._ui.$errorsWrapper);
  },

  actualizeTriggers: function (fieldInstance) {
    var $toBind = fieldInstance.$element;
    if (fieldInstance.options.multiple)
      $toBind = $('[' + fieldInstance.options.namespace + 'multiple="' + fieldInstance.options.multiple + '"]');

    // Remove Parsley events already binded on this field
    $toBind.off('.Parsley');

    // If no trigger is set, all good
    if (false === fieldInstance.options.trigger)
      return;

    var triggers = fieldInstance.options.trigger.replace(/^\s+/g , '').replace(/\s+$/g , '');

    if ('' === triggers)
      return;

    // Bind fieldInstance.eventValidate if exists (for parsley.ajax for example), ParsleyUI.eventValidate otherwise
    $toBind.on(
      triggers.split(' ').join('.Parsley ') + '.Parsley',
      $.proxy('function' === typeof fieldInstance.eventValidate ? fieldInstance.eventValidate : this.eventValidate, fieldInstance));
  },

  // Called through $.proxy with fieldInstance. `this` context is ParsleyField
  eventValidate: function (event) {
    // For keyup, keypress, keydown... events that could be a little bit obstrusive
    // do not validate if val length < min threshold on first validation. Once field have been validated once and info
    // about success or failure have been displayed, always validate with this trigger to reflect every yalidation change.
    if (/key/.test(event.type))
      if (!this._ui.validationInformationVisible && this.getValue().length <= this.options.validationThreshold)
        return;

    this._ui.validatedOnce = true;
    this.validate();
  },

  manageFailingFieldTrigger: function (fieldInstance) {
    fieldInstance._ui.failedOnce = true;

    // Radio and checkboxes fields must bind every field multiple
    if (fieldInstance.options.multiple)
      $('[' + fieldInstance.options.namespace + 'multiple="' + fieldInstance.options.multiple + '"]').each(function () {
        if (!/change/i.test($(this).parsley().options.trigger || ''))
          $(this).on('change.ParsleyFailedOnce', false, () => { fieldInstance.validate(); });
      });

    // Select case
    if (fieldInstance.$element.is('select'))
      if (!/change/i.test(fieldInstance.options.trigger || ''))
        return fieldInstance.$element.on('change.ParsleyFailedOnce', false, () => { fieldInstance.validate(); });

    // All other inputs fields
    if (!/keyup/i.test(fieldInstance.options.trigger || ''))
      return fieldInstance.$element.on('keyup.ParsleyFailedOnce', false, () => { fieldInstance.validate(); });
  },

  reset: function (parsleyInstance) {
    // Reset all event listeners
    this.actualizeTriggers(parsleyInstance);
    parsleyInstance.$element.off('.ParsleyFailedOnce');

    // Nothing to do if UI never initialized for this field
    if ('undefined' === typeof parsleyInstance._ui)
      return;

    if ('ParsleyForm' === parsleyInstance.__class__)
      return;

    // Reset all errors' li
    parsleyInstance._ui.$errorsWrapper
      .removeClass('filled')
      .children()
      .remove();

    // Reset validation class
    this._resetClass(parsleyInstance);

    // Reset validation flags and last validation result
    parsleyInstance._ui.validatedOnce = false;
    parsleyInstance._ui.lastValidationResult = [];
    parsleyInstance._ui.validationInformationVisible = false;
    parsleyInstance._ui.failedOnce = false;
  },

  destroy: function (parsleyInstance) {
    this.reset(parsleyInstance);

    if ('ParsleyForm' === parsleyInstance.__class__)
      return;

    if ('undefined' !== typeof parsleyInstance._ui)
      parsleyInstance._ui.$errorsWrapper.remove();

    delete parsleyInstance._ui;
  },

  _successClass: function (fieldInstance) {
    fieldInstance._ui.validationInformationVisible = true;
    fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.errorClass).addClass(fieldInstance.options.successClass);
  },
  _errorClass: function (fieldInstance) {
    fieldInstance._ui.validationInformationVisible = true;
    fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.successClass).addClass(fieldInstance.options.errorClass);
  },
  _resetClass: function (fieldInstance) {
    fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.successClass).removeClass(fieldInstance.options.errorClass);
  }
};

export default ParsleyUI;
<|endoftext|>


<commit_before>addEventListener("message", msg_handler, false);
addEventListener("error", err_handler, false);

var workerData = {
	id: location["search"].slice(1)
};

function msg_handler(e) {
	postMessage(workerData);
	workerData.fn = e.data.fn;

	if(!!e.data && !!e.data.fn) {
		(function() {
			this[e.data.fn](e.data);
		})();
	}
}

function err_handler(e) {
	postMessage("Error @: ", e.lineno, "\n\t File: ", e.filename, "\n\t Message:", e.message);
}

function createCard(jsonData) {
	var msg = jsonData["msg"];
	for(var key in msg) {
		var p = msg.position;
		var card = "\n" + "
" +
			"
" + msg.character + "
" +
			contextualForms(msg["contextualForms"]) +
			tags(msg["tags"]) +
			"
" + "\n";
	}

	function contextualForms(obj) {
		// use &#x....; to use unicode in HTML markup
		var _forms = "
";
		for(var key in obj) {
			if(obj[key] !== "") {
				_forms += "
" + "&#x" + obj[key] + ";
";
			}
		}
		return _forms + "
";
	}

	function tags(arr) {
		var i = 0;
		var len = arr.length;
		var _tags = "
";
		for(i; i < len; i++) {
			_tags += "
" + arr[i] + "
";
		}
		return _tags + "
";
	}

	workerData.msg = card;
	// set / post each card back to main thread
	postMessage(workerData);
}

function getLocation(part) {
	return !!part? location[part].toString(): location.toString();
}<commit_msg>updated card properties to include data-sound<commit_after>addEventListener("message", msg_handler, false);
addEventListener("error", err_handler, false);

var workerData = {
	id: location["search"].slice(1)
};

function msg_handler(e) {
	postMessage(workerData);
	workerData.fn = e.data.fn;

	if(!!e.data && !!e.data.fn) {
		(function() {
			this[e.data.fn](e.data);
		})();
	}
}

function err_handler(e) {
	postMessage("Error @: ", e.lineno, "\n\t File: ", e.filename, "\n\t Message:", e.message);
}

function createCard(jsonData) {
	var msg = jsonData["msg"];
	for(var key in msg) {
		var p = msg.position;
		var card = "\n" + "
" +
			"
" + msg.character + "
" +
			contextualForms(msg["contextualForms"]) +
			tags(msg["tags"]) +
			"
" + "\n";
	}

	function contextualForms(obj) {
		// use &#x....; to use unicode in HTML markup
		var _forms = "
";
		for(var key in obj) {
			if(obj[key] !== "") {
				_forms += "
" + "&#x" + obj[key] + ";
";
			}
		}
		return _forms + "
";
	}

	function tags(arr) {
		var i = 0;
		var len = arr.length;
		var _tags = "
";
		for(i; i < len; i++) {
			_tags += "
" + arr[i] + "
";
		}
		return _tags + "
";
	}

	workerData.msg = card;
	// set / post each card back to main thread
	postMessage(workerData);
}

function getLocation(part) {
	return !!part? location[part].toString(): location.toString();
}<|endoftext|>


<commit_before>/**
 * Copyright 2013 massimocorner.com
 * @author      Massimo Foti (massimo@massimocorner.com)
 * @require     luga.js
 */

"use strict";
if(typeof(luga) === "undefined") {
	throw("Unable to find luga.js");
}
luga.namespace("luga.validator");
luga.namespace("luga.validator.util");

luga.validator.CONST = {
	FORM_SELECTOR: "form[data-luga-validate]",
	ATTRIBUTES: {
		VALIDATE: "data-luga-validate",
		BLOCK_SUBMIT: "data-luga-blocksubmit"
	},
	FAKE_INPUT_TYPES: {
		fieldset: true,
		reset: true,
		button: true,
		submit: true
	}
};

/**
 * Form validator widget
 *
 * @param options.formNode:          Root node for widget (DOM reference). Required
 * @param options.blocksubmit:       Disable submit buttons if the form isn't valid.
 *                                   This prevents multiple submits but also prevents the value of the submit buttons from being passed as part of the HTTP request.
 *                                   Set this options to false to keep the submit buttons enabled. Optional
 */
luga.validator.formValidator = function(options) {

	this.options = {
		blocksubmit: jQuery(options.formNode).attr(luga.validator.CONST.ATTRIBUTES.BLOCK_SUBMIT) || true
	};
	jQuery.extend(this.options, options);
	var self = this;
	self.validators = [];

	// Execute all field validators. Returns an array of field validators that are in invalid state
	// Returns and empty array if no errors
	this.executeValidators = function() {
		// Keep track of already validated fields (to skip already validated checkboxes or radios)
		var validatedFields = {};
		var activeValidators = [];
		// Validate all the fields
		for(var i=0; i<self.validators.length; i++) {
			if(self.validators[i].validate) {
				if(validatedFields[self.validators[i].name]) {
					// Already validated checkbox or radio, skip it
					continue;
				}
				if(self.validators[i].validate()) {
					activeValidators[activeValidators.length] = self.validators[i];
				}
				validatedFields[self.validators[i].name] = true;
			}
		}
		return activeValidators;
	};

	this.isValid = function() {
		var activeValidators = this.executeValidators();
		return activeValidators.length === 0;
	};

};

luga.validator.util.isInputField = function(fieldNode) {
	if(!jQuery(fieldNode).prop("type")) {
		return false;
	}
	if(luga.validator.CONST.FAKE_INPUT_TYPES[jQuery(fieldNode).prop("type")] === true) {
		return false;
	}
	return true;
};<commit_msg>Tweak comments<commit_after>/**
 * Copyright 2013 massimocorner.com
 * @author      Massimo Foti (massimo@massimocorner.com)
 * @require     luga.js
 */

"use strict";
if(typeof(luga) === "undefined") {
	throw("Unable to find luga.js");
}
luga.namespace("luga.validator");
luga.namespace("luga.validator.util");

luga.validator.CONST = {
	FORM_SELECTOR: "form[data-luga-validate]",
	ATTRIBUTES: {
		VALIDATE: "data-luga-validate",
		BLOCK_SUBMIT: "data-luga-blocksubmit"
	},
	FAKE_INPUT_TYPES: {
		fieldset: true,
		reset: true,
		button: true,
		submit: true
	}
};

/**
 * Form validator widget
 *
 * @param options.formNode:          Root node for widget (DOM reference). Required
 * @param options.blocksubmit:       Disable submit buttons if the form isn't valid.
 *                                   This prevents multiple submits but also prevents the value of the submit buttons from being passed as part of the HTTP request.
 *                                   Set this options to false to keep the submit buttons enabled. Optional
 */
luga.validator.formValidator = function(options) {

	this.options = {
		blocksubmit: jQuery(options.formNode).attr(luga.validator.CONST.ATTRIBUTES.BLOCK_SUBMIT) || true
	};
	jQuery.extend(this.options, options);
	var self = this;
	self.validators = [];

	// Execute all field validators. Returns an array of field validators that are in invalid state
	// Returns and empty array if no errors
	this.executeValidators = function() {
		// Keep track of already validated fields (to skip already validated checkboxes or radios)
		var validatedFields = {};
		var activeValidators = [];
		// Validate all the fields
		for(var i=0; i<self.validators.length; i++) {
			if(self.validators[i].validate) {
				if(validatedFields[self.validators[i].name]) {
					// Already validated checkbox or radio, skip it
					continue;
				}
				if(self.validators[i].validate()) {
					activeValidators[activeValidators.length] = self.validators[i];
				}
				validatedFields[self.validators[i].name] = true;
			}
		}
		return activeValidators;
	};

	this.isValid = function() {
		var activeValidators = this.executeValidators();
		return activeValidators.length === 0;
	};

};

luga.validator.util.isInputField = function(fieldNode) {
	if(!jQuery(fieldNode).prop("type")) {
		return false;
	}
	// It belongs to the kind of nodes that are considered form fields, but can't be validated
	if(luga.validator.CONST.FAKE_INPUT_TYPES[jQuery(fieldNode).prop("type")] === true) {
		return false;
	}
	return true;
};<|endoftext|>


<commit_before>"use strict"

/**
 * Diff module
 */
define([],
       function() {

            /**
             * A set of diff objects
             */
            function Diff_Set(obj_spec) {
                this.__diff_set_topo = [];
                this.__diff_set_attr = [];
                this.__diff_set_vis = [];
            }
            Diff_Set.prototype.add_diff_obj = function(diff_obj) {
                if (diff_obj instanceof Topo_Diff) {
                    this.__diff_set_topo.push(diff_obj);
                }
                if (diff_obj instanceof Attr_Diff) {
                    this.__diff_set_attr.push(diff_obj);
                }
                if (diff_obj instanceof Vis_Diff) {
                    this.__diff_set_vis.push(diff_obj);
                }
            }

            /**
             * Topological diff object
             */
            function Topo_Diff(obj_spec) {

                this.link_set_rm = obj_spec.link_set_rm;
                this.node_set_rm = obj_spec.node_set_rm;
                this.node_set_add = obj_spec.node_set_add;
                this.link_set_add = obj_spec.link_set_add;
            }

            /**
             * Attribute diff object, organized by type, where currently
             * node,link types are supported
             */
            function Attr_Diff(obj_spec) {
                this.__type_node = {}
                this.__type_link = {}
            }

            Attr_Diff.prototype.init_attr_diff = function(type, id) {

                if ('node' != type && 'link' != type) {
                    console
                            .error('attempt to init attribute diff for unsupported type: '
                                    + type);
                    return;
                }

                var type_field = '__type_' + type;
                this[type_field][id] = {
                    '__attr_write' : {},
                    '__attr_remove' : []
                };

                return this;
            }

            Attr_Diff.prototype.init_attr_diff_node = function(id) {
                return this.init_attr_diff('node', id);
            }

            Attr_Diff.prototype.init_attr_diff_link = function(id) {
                return this.init_attr_diff('link', id);
            }

            Attr_Diff.prototype.add_attr_write_node = function(n_id, attr_name,
                    attr_val) {

                if (undefined == this.__type_node[n_id]) {
                    this.init_attr_diff_node(n_id);
                }
                this.__type_node[n_id].__attr_write[attr_name] = attr_val;
                return this;
            }

            Attr_Diff.prototype.add_attr_rm_node = function(n_id, attr_name) {
                if (undefined == this[n_id]) {
                    this.init_attr_diff(n_id);
                }
                this.__type_node[n_id].__attr_remove.push(attr_name);
                return this;
            }

            /**
             * Visual diff object expressing any visual change to the state of a
             * particular visualization type
             */
            function Vis_Diff(obj_spec) {
            }

            function new_topo_diff(obj_spec) {
                /*
                 * validate obj_spec
                 */
                // TODO
                var ret = new Topo_Diff(obj_spec);
                return ret;
            }

            function new_attr_diff(obj_spec) {
                /*
                 * validate obj_spec
                 */
                // TODO
                var ret = new Attr_Diff(obj_spec);
                ret.__type_node = {}; // id-to-obj map
                ret.__type_link = {}; // id-to-obj map
                return ret;
            }

            function new_vis_diff(obj_spec) {
                /*
                 * validate obj_spec
                 */
                // TODO
                var ret = new Vis_Diff(obj_spec);
                return ret;
            }

            function new_diff_set(obj_spec) {
                /*
                 * validate obj_spec
                 */
                // TODO
                var ret = new Diff_Set(obj_spec);
                return ret;
            }

            return {
                new_topo_diff : new_topo_diff,
                new_attr_diff : new_attr_diff,
                new_vis_diff : new_vis_diff,
                new_diff_set : new_diff_set,
            }
        });<commit_msg>Topo_Diff - for_each_X() convenience functions<commit_after>"use strict"

/**
 * Diff module
 */
define([],
       function() {

            /**
             * A set of diff objects
             */
            function Diff_Set(obj_spec) {
                this.__diff_set_topo = [];
                this.__diff_set_attr = [];
                this.__diff_set_vis = [];
            }
            Diff_Set.prototype.add_diff_obj = function(diff_obj) {
                if (diff_obj instanceof Topo_Diff) {
                    this.__diff_set_topo.push(diff_obj);
                }
                if (diff_obj instanceof Attr_Diff) {
                    this.__diff_set_attr.push(diff_obj);
                }
                if (diff_obj instanceof Vis_Diff) {
                    this.__diff_set_vis.push(diff_obj);
                }
            }

            /**
             * Topological diff object
             */
            function Topo_Diff(obj_spec) {

                this.link_set_rm = obj_spec.link_set_rm;
                this.node_set_rm = obj_spec.node_set_rm;
                this.node_set_add = obj_spec.node_set_add;
                this.link_set_add = obj_spec.link_set_add;

            }
            Topo_Diff.prototype.for_each_node_add = function(callback, this_arg) {
                this.node_set_add.forEach(callback, this_arg);
            }

            Topo_Diff.prototype.for_each_node_rm = function(callback, this_arg) {
                this.node_set_rm.forEach(callback, this_arg);
            }

            Topo_Diff.prototype.for_each_link_add = function(callback, this_arg) {
                this.link_set_add.forEach(callback, this_arg);
            }

            Topo_Diff.prototype.for_each_link_rm = function(callback, this_arg) {
                this.link_set_rm.forEach(callback, this_arg);
            }

            /**
             * Attribute diff object, organized by type, where currently
             * node,link types are supported
             */
            function Attr_Diff(obj_spec) {
                this.__type_node = {}
                this.__type_link = {}
            }

            Attr_Diff.prototype.init_attr_diff = function(type, id) {

                if ('node' != type && 'link' != type) {
                    console
                            .error('attempt to init attribute diff for unsupported type: '
                                    + type);
                    return;
                }

                var type_field = '__type_' + type;
                this[type_field][id] = {
                    '__attr_write' : {},
                    '__attr_remove' : []
                };

                return this;
            }

            Attr_Diff.prototype.init_attr_diff_node = function(id) {
                return this.init_attr_diff('node', id);
            }

            Attr_Diff.prototype.init_attr_diff_link = function(id) {
                return this.init_attr_diff('link', id);
            }

            Attr_Diff.prototype.add_attr_write_node = function(n_id, attr_name,
                    attr_val) {

                if (undefined == this.__type_node[n_id]) {
                    this.init_attr_diff_node(n_id);
                }
                this.__type_node[n_id].__attr_write[attr_name] = attr_val;
                return this;
            }

            Attr_Diff.prototype.add_attr_rm_node = function(n_id, attr_name) {
                if (undefined == this[n_id]) {
                    this.init_attr_diff(n_id);
                }
                this.__type_node[n_id].__attr_remove.push(attr_name);
                return this;
            }

            /**
             * Visual diff object expressing any visual change to the state of a
             * particular visualization type
             */
            function Vis_Diff(obj_spec) {
            }

            function new_topo_diff(obj_spec) {
                /*
                 * validate obj_spec
                 */
                // TODO
                var ret = new Topo_Diff(obj_spec);
                return ret;
            }

            function new_attr_diff(obj_spec) {
                /*
                 * validate obj_spec
                 */
                // TODO
                var ret = new Attr_Diff(obj_spec);
                ret.__type_node = {}; // id-to-obj map
                ret.__type_link = {}; // id-to-obj map
                return ret;
            }

            function new_vis_diff(obj_spec) {
                /*
                 * validate obj_spec
                 */
                // TODO
                var ret = new Vis_Diff(obj_spec);
                return ret;
            }

            function new_diff_set(obj_spec) {
                /*
                 * validate obj_spec
                 */
                // TODO
                var ret = new Diff_Set(obj_spec);
                return ret;
            }

            return {
                new_topo_diff : new_topo_diff,
                new_attr_diff : new_attr_diff,
                new_vis_diff : new_vis_diff,
                new_diff_set : new_diff_set,
            }
        });<|endoftext|>


<commit_before>/**
 * Patterns stacks
 *
 * Copyright 2013 Simplon B.V. - Wichert Akkerman
 */
define([
    "jquery",
    "../core/parser",
    "../registry"
], function($, Parser, registry) {
    var parser = new Parser("stacks");

    parser.add_argument("transition", "none", ["none", "css", "fade", "slide"]);
    parser.add_argument("effect-duration", "fast");
    parser.add_argument("selector", "> *[id]");

    var stacks = {
        name: "stacks",
        trigger: ".pat-stacks",
        _triggers: {},

        init: function($el, opts) {
            var fragment = this._currentFragment();

            $(document).on("click", "a", this._onClick);

            return $el.each(function() {
                var $container = $(this),
                    options = parser.parse($container, opts),
                    $leaves = $container.find(options.selector),
                    $visible, $hidden;
                if ($leaves.length<2)
                    return;
                $container.data("pat-stacks", options);

                if (fragment && $leaves.filter(fragment).length)
                    $visible=$leaves.filter(fragment);
                else
                    $visible=$leaves.first();
                $invisible=$leaves.not($visible);

                $visible.addClass("visible").removeClass("hidden").show();
                $invisible.removeClass("visible").addClass("hidden").hide();

                $leaves.each(function() {
                    stacks._triggers["#"+this.id]=$container;
                });
            });
        },

        _currentFragment: function() {
            var parts = document.URL.split("#");
            if (parts.length===1)
                return null;
            return "#"+parts[parts.length-1];
        },

        _onClick: function(e) {
            var base_url = document.URL.split("#")[0],
                href = e.target.href.replace(base_url, ""),
                $container=stacks._triggers[href];
            if ($container===undefined)
                return;
            e.preventDefault();
            stacks._switch($container, href);
        },

        _switch: function($container, leave_id) {
            var $leave = $container.find(leave_id);
            if (!$leave.length || $leave.hasClass("visible"))
                return;

            var options = $container.data("pat-stacks"),
                $invisible = $container.find(options.selector).not($leave);
            $leave.addClass("visible").removeClass("hidden").show();
            $invisible.removeClass("visible").addClass("hidden").hide();
        }
    };

    registry.register(stacks);
    return stacks;
});
<commit_msg>Stacks: update anchor classes.<commit_after>/**
 * Patterns stacks
 *
 * Copyright 2013 Simplon B.V. - Wichert Akkerman
 */
define([
    "jquery",
    "../core/parser",
    "../core/logger",
    "../registry"
], function($, Parser, logging, registry) {
    var log = logging.getLogger("stacks"),
        parser = new Parser("stacks");

    parser.add_argument("transition", "none", ["none", "css", "fade", "slide"]);
    parser.add_argument("effect-duration", "fast");
    parser.add_argument("selector", "> *[id]");

    var stacks = {
        name: "stacks",
        trigger: ".pat-stacks",
        _triggers: {},

        init: function($el, opts) {
            var fragment = this._currentFragment();

            return $el.each(function() {
                stacks._setupStack(this, opts, fragment);
            });
        },

        _setup: function() {
            $(document).on("click", "a", this._onClick);
        },

        _setupStack: function(container, options, selected) {
            var $container = $(container),
                $leaves, $visible, $invisible;
            options=parser.parse($container, options);
            $container.data("pat-stacks", options);
            $leaves=$container.find(options.selector);
            if ($leaves.length<2) {
                log.warn("Stacks pattern: must have more than one leaf.", $container[0]);
                return;
            }

            $visible=[];
            if (selected)
                $visible=$leaves.filter("#"+selected);
            if (!$visible.length) {
                $visible=$leaves.first();
                selected=$visible[0].id;
            }
            $invisible=$leaves.not($visible);
            $visible.addClass("visible").removeClass("hidden").show();
            $invisible.removeClass("visible").addClass("hidden").hide();
            stacks._updateAnchors($container, selected);
        },

         _base_URL: function() {
            return document.URL.split("#")[0];
         },

        _currentFragment: function() {
            var parts = document.URL.split("#");
            if (parts.length===1)
                return null;
            return parts[parts.length-1];
        },

        _onClick: function(e) {
            var base_url = stacks._base_URL(),
                href_parts = e.target.href.split("#"),
                $stack;
            // Check if this is an in-document link and has a fragment
            if (base_url!==href_parts[0] || !href_parts[1])
                return;
            $stack=$(stacks.trigger+":has(#"+href_parts[1]+")");
            if (!$stack.length)
                return;
            e.preventDefault();
            stacks._updateAnchors($stack, href_parts[1]);
            stacks._switch($stack, href_parts[1]);
        },

        _updateAnchors: function($container, selected) {
            var options = $container.data("pat-stacks"),
                $leaves = $container.find(options.selector),
                base_url = stacks._base_URL();
            for (var i=0; i<$leaves.length; i++) {
                var leaf = $leaves[i],
                    $anchors = $("a[href=\""+base_url+"#"+leaf.id+"\"],a[href=\"#"+leaf.id+"\"]");
                if (leaf.id===selected)
                    $anchors.addClass("current");
                else
                    $anchors.removeClass("current");
            }
        },

        _switch: function($container, leaf_id) {
            var $leaf = $container.find("#"+leaf_id);
            if (!$leaf.length || $leaf.hasClass("visible"))
                return;

            var options = $container.data("pat-stacks"),
                $invisible = $container.find(options.selector).not($leaf);
            $leaf.addClass("visible").removeClass("hidden").show();
            $invisible.removeClass("visible").addClass("hidden").hide();
        }
    };

    stacks._setup();
    registry.register(stacks);
    return stacks;
});
<|endoftext|>