<commit_before>(function(undefined) {

    // The default loader is responsible for two things:
    // 1. Maintaining the default in-memory registry of component configuration objects
    //    (i.e., the thing you're writing to when you call ko.components.register(someName, ...))
    // 2. Answering requests for components by fetching configuration objects
    //    from that default in-memory registry and resolving them into standard
    //    component definition objects (of the form { createViewModel: ..., template: ... })
    // Custom loaders may override either of these facilities, i.e.,
    // 1. To supply configuration objects from some other source (e.g., conventions)
    // 2. Or, to resolve configuration objects by loading viewmodels/templates via arbitrary logic.

    var defaultConfigRegistry = {};

    ko.components.register = function(componentName, config) {
        if (!config) {
            throw new Error('Invalid configuration for ' + componentName);
        }

        if (ko.components.isRegistered(componentName)) {
            throw new Error('Component ' + componentName + ' is already registered');
        }

        defaultConfigRegistry[componentName] = config;
    }

    ko.components.isRegistered = function(componentName) {
        return componentName in defaultConfigRegistry;
    }

    ko.components.unregister = function(componentName) {
        delete defaultConfigRegistry[componentName];
        ko.components.clearCachedDefinition(componentName);
    }

    ko.components.defaultLoader = {
        'getConfig': function(componentName, callback) {
            var result = defaultConfigRegistry.hasOwnProperty(componentName)
                ? defaultConfigRegistry[componentName]
                : null;
            callback(result);
        },

        'loadComponent': function(componentName, config, callback) {
            var errorMessagePrefix = 'Component \'' + componentName + '\': ';

            if (typeof config['require'] === 'string') {
                // The config is the value of an AMD module
                requireAmdModule(errorMessagePrefix, config['require'], function(module) {
                    resolveConfig(errorMessagePrefix, componentName, module, callback);
                });
            } else {
                // The config is a { template: ..., viewModel: ... } object
                resolveConfig(errorMessagePrefix, componentName, config, callback);
            }
        }
    };

    // Takes a config object of the form { template: ..., viewModel: ... }, and asynchronously convert it
    // into the standard component definition format:
    //    { template: documentFragment, createViewModel: function(componentInfo, params) { ... } }.
    // Since both template and viewModel may need to be resolved asynchronously, both tasks are performed
    // in parallel, and the results joined when both are ready. We don't depend on any promises infrastructure,
    // so this is implemented manually below.
    function resolveConfig(errorMessagePrefix, componentName, config, callback) {
        var result = {},
            hasResolvedTemplate = false,
            hasResolvedViewModel = false,
            hasIssuedCallback = false,
            tryIssueCallback = function() {
                if (hasResolvedTemplate && hasResolvedViewModel && !hasIssuedCallback) {
                    hasIssuedCallback = true;
                    callback(result);
                }
            };

        if (config['template']) {
            resolveTemplate(errorMessagePrefix, config['template'], function(resolvedTemplate) {
                result['template'] = resolvedTemplate;
                hasResolvedTemplate = true;
                tryIssueCallback();
            });
        } else {
            hasResolvedTemplate = true;
        }

        if (config['viewModel']) {
            resolveViewModel(errorMessagePrefix, config['viewModel'], function(resolvedViewModel) {
                result['createViewModel'] = resolvedViewModel;
                hasResolvedViewModel = true;
                tryIssueCallback();
            });
        } else {
            hasResolvedViewModel = true;
        }

        // Handle the case where neither template nor viewmodel is defined, or
        // where one of them is not defined and the other completes synchronously.
        tryIssueCallback();
    }

    function err(message) {
        throw new Error(message);
    }

    function resolveTemplate(errorMessagePrefix, templateConfig, callback) {
        if (typeof templateConfig === 'string') {
            // Markup - parse it
            var nodeArray = ko.utils.parseHtmlFragment(templateConfig);
            callback(elementListToDocumentFragment(nodeArray, false /* shouldClone */));
        } else if (isDocumentFragment(templateConfig)) {
            // Pass through document fragments unchanged
            callback(templateConfig);
        } else if (templateConfig['element']) {
            var element = templateConfig['element'];
            if (isDomElement(element)) {
                // Element instance - use its child nodes
                callback(elementListToDocumentFragment(element.childNodes, true /* shouldClone */));
            } else if (typeof element === 'string') {
                // Element ID - find it, then use its child nodes
                var elemInstance = document.getElementById(element);
                if (elemInstance) {
                    callback(elementListToDocumentFragment(elemInstance.childNodes, true /* shouldClone */));
                } else {
                    err(errorMessagePrefix + 'Cannot find element with ID ' + templateConfig);
                }
            } else {
                err(errorMessagePrefix + 'Unknown element type: ' + element);
            }
        } else if (typeof templateConfig['require'] === 'string') {
            // AMD module
            requireAmdModule(errorMessagePrefix, templateConfig['require'], function(module) {
                // Continue resolution using the module value, which might be a document fragment,
                // a markup string, or even a DOM element instance
                resolveTemplate(errorMessagePrefix, module, callback);
            });
        } else {
            err(errorMessagePrefix + 'Unknown template value: ' + templateConfig);
        }
    }

    var createViewModelKey = 'createViewModel';

    function resolveViewModel(errorMessagePrefix, viewModelConfig, callback) {
        if (typeof viewModelConfig === 'function') {
            // Constructor - convert to standard factory function format
            // By design, this does *not* supply componentInfo to the constructor, as the intent is that
            // componentInfo contains non-viewmodel data (e.g., the component's element) that should only
            // be used in factory functions, not viewmodel constructors.
            callback(function (componentInfo, params) {
                return new viewModelConfig(params);
            });
        } else if (typeof viewModelConfig[createViewModelKey] === 'function') {
            // Already a factory function - use it as-is
            callback(viewModelConfig[createViewModelKey]);
        } else if ('instance' in viewModelConfig) {
            // Fixed object instance - promote to createViewModel format for API consistency
            var fixedInstance = viewModelConfig['instance'];
            callback(function (componentInfo, params) {
                return fixedInstance;
            });
        } else if (typeof viewModelConfig['require'] === 'string') {
            // AMD module
            requireAmdModule(errorMessagePrefix, viewModelConfig['require'], function(module) {
                // Continue resolution using the module value, which might be of any
                // of the allowed configuration formats for viewmodels
                resolveViewModel(errorMessagePrefix, module, callback);
            });
        } else if ('viewModel' in viewModelConfig) {
            // Resolved AMD module whose value is of the form { viewModel: ... }
            resolveViewModel(errorMessagePrefix, viewModelConfig['viewModel'], callback);
        } else {
            err(errorMessagePrefix + 'Unknown viewModel value: ' + viewModelConfig);
        }
    }

    function isDomElement(obj) {
        if (window.HTMLElement) {
            return obj instanceof HTMLElement;
        } else {
            return obj && obj.tagName && obj.nodeType === 1;
        }
    }

    function isDocumentFragment(obj) {
        if (window.DocumentFragment) {
            return obj instanceof DocumentFragment;
        } else {
            return obj && obj.nodeType === 11;
        }
    }

    function elementListToDocumentFragment(elementList, shouldClone) {
        // elementList can be a real array, or the .childNodes property of a DOM element
        var docFrag = document.createDocumentFragment();
        for (var i = 0, j = elementList.length; i < j; i++) {
            docFrag.appendChild(shouldClone ? elementList[i].cloneNode(true) : elementList[i]);
        }
        return docFrag;
    }

    function requireAmdModule(errorMessagePrefix, amdModuleName, callback) {
        if (window['require']) {
            require([amdModuleName], callback);
        } else {
            err(errorMessagePrefix + 'Uses require, but no AMD loader is present');
        }
    }

    ko.exportSymbol('components.register', ko.components.register);
    ko.exportSymbol('components.isRegistered', ko.components.isRegistered);
    ko.exportSymbol('components.unregister', ko.components.unregister);

    // Expose the default loader so that developers can directly ask it for configuration
    // or to resolve configuration
    ko.exportSymbol('components.defaultLoader', ko.components.defaultLoader);

    // By default, the default loader is the only registered component loader
    ko.components['loaders'].push(ko.components.defaultLoader);
})();
<commit_msg>default loader - use error callback instead of prefix string; move AMD logic out of the template and viewmodel loaders; simplify parallel loading logic.<commit_after>(function(undefined) {

    // The default loader is responsible for two things:
    // 1. Maintaining the default in-memory registry of component configuration objects
    //    (i.e., the thing you're writing to when you call ko.components.register(someName, ...))
    // 2. Answering requests for components by fetching configuration objects
    //    from that default in-memory registry and resolving them into standard
    //    component definition objects (of the form { createViewModel: ..., template: ... })
    // Custom loaders may override either of these facilities, i.e.,
    // 1. To supply configuration objects from some other source (e.g., conventions)
    // 2. Or, to resolve configuration objects by loading viewmodels/templates via arbitrary logic.

    var defaultConfigRegistry = {};

    ko.components.register = function(componentName, config) {
        if (!config) {
            throw new Error('Invalid configuration for ' + componentName);
        }

        if (ko.components.isRegistered(componentName)) {
            throw new Error('Component ' + componentName + ' is already registered');
        }

        defaultConfigRegistry[componentName] = config;
    }

    ko.components.isRegistered = function(componentName) {
        return componentName in defaultConfigRegistry;
    }

    ko.components.unregister = function(componentName) {
        delete defaultConfigRegistry[componentName];
        ko.components.clearCachedDefinition(componentName);
    }

    ko.components.defaultLoader = {
        'getConfig': function(componentName, callback) {
            var result = defaultConfigRegistry.hasOwnProperty(componentName)
                ? defaultConfigRegistry[componentName]
                : null;
            callback(result);
        },

        'loadComponent': function(componentName, config, callback) {
            function errorCallback(message) {
                throw new Error('Component \'' + componentName + '\': ' + message);
            }

            possiblyGetConfigFromAmd(errorCallback, config, function(loadedConfig) {
                resolveConfig(errorCallback, componentName, loadedConfig, callback);
            });
        }
    };

    // Takes a config object of the form { template: ..., viewModel: ... }, and asynchronously convert it
    // into the standard component definition format:
    //    { template: documentFragment, createViewModel: function(componentInfo, params) { ... } }.
    // Since both template and viewModel may need to be resolved asynchronously, both tasks are performed
    // in parallel, and the results joined when both are ready. We don't depend on any promises infrastructure,
    // so this is implemented manually below.
    function resolveConfig(errorCallback, componentName, config, callback) {
        var result = {},
            makeCallBackWhenZero = 2,
            tryIssueCallback = function() {
                if (--makeCallBackWhenZero === 0) {
                    callback(result);
                }
            },
            templateConfig = config['template'],
            viewModelConfig = config['viewModel'];

        if (templateConfig) {
            possiblyGetConfigFromAmd(errorCallback, templateConfig, function(loadedConfig) {
                resolveTemplate(errorCallback, loadedConfig, function(resolvedTemplate) {
                    result['template'] = resolvedTemplate;
                    tryIssueCallback();
                });
            });
        } else {
            tryIssueCallback();
        }

        if (viewModelConfig) {
            possiblyGetConfigFromAmd(errorCallback, viewModelConfig, function(loadedConfig) {
                resolveViewModel(errorCallback, loadedConfig, function(resolvedViewModel) {
                    result['createViewModel'] = resolvedViewModel;
                    tryIssueCallback();
                });
            });
        } else {
            tryIssueCallback();
        }
    }

    function resolveTemplate(errorCallback, templateConfig, callback) {
        if (typeof templateConfig === 'string') {
            // Markup - parse it
            var nodeArray = ko.utils.parseHtmlFragment(templateConfig);
            callback(elementListToDocumentFragment(nodeArray, false /* shouldClone */));
        } else if (isDocumentFragment(templateConfig)) {
            // Pass through document fragments unchanged
            callback(templateConfig);
        } else if (templateConfig['element']) {
            var element = templateConfig['element'];
            if (isDomElement(element)) {
                // Element instance - use its child nodes
                callback(elementListToDocumentFragment(element.childNodes, true /* shouldClone */));
            } else if (typeof element === 'string') {
                // Element ID - find it, then use its child nodes
                var elemInstance = document.getElementById(element);
                if (elemInstance) {
                    callback(elementListToDocumentFragment(elemInstance.childNodes, true /* shouldClone */));
                } else {
                    errorCallback('Cannot find element with ID ' + element);
                }
            } else {
                errorCallback('Unknown element type: ' + element);
            }
        } else {
            errorCallback('Unknown template value: ' + templateConfig);
        }
    }

    var createViewModelKey = 'createViewModel';

    function resolveViewModel(errorCallback, viewModelConfig, callback) {
        if (typeof viewModelConfig === 'function') {
            // Constructor - convert to standard factory function format
            // By design, this does *not* supply componentInfo to the constructor, as the intent is that
            // componentInfo contains non-viewmodel data (e.g., the component's element) that should only
            // be used in factory functions, not viewmodel constructors.
            callback(function (componentInfo, params) {
                return new viewModelConfig(params);
            });
        } else if (typeof viewModelConfig[createViewModelKey] === 'function') {
            // Already a factory function - use it as-is
            callback(viewModelConfig[createViewModelKey]);
        } else if ('instance' in viewModelConfig) {
            // Fixed object instance - promote to createViewModel format for API consistency
            var fixedInstance = viewModelConfig['instance'];
            callback(function (componentInfo, params) {
                return fixedInstance;
            });
        } else if ('viewModel' in viewModelConfig) {
            // Resolved AMD module whose value is of the form { viewModel: ... }
            resolveViewModel(errorCallback, viewModelConfig['viewModel'], callback);
        } else {
            errorCallback('Unknown viewModel value: ' + viewModelConfig);
        }
    }

    function isDomElement(obj) {
        if (window.HTMLElement) {
            return obj instanceof HTMLElement;
        } else {
            return obj && obj.tagName && obj.nodeType === 1;
        }
    }

    function isDocumentFragment(obj) {
        if (window.DocumentFragment) {
            return obj instanceof DocumentFragment;
        } else {
            return obj && obj.nodeType === 11;
        }
    }

    function elementListToDocumentFragment(elementList, shouldClone) {
        // elementList can be a real array, or the .childNodes property of a DOM element
        var docFrag = document.createDocumentFragment();
        for (var i = 0, j = elementList.length; i < j; i++) {
            docFrag.appendChild(shouldClone ? elementList[i].cloneNode(true) : elementList[i]);
        }
        return docFrag;
    }

    function possiblyGetConfigFromAmd(errorCallback, config, callback) {
        if (typeof config['require'] === 'string') {
            // The config is the value of an AMD module
            if (window['require']) {
                require([config['require']], callback);
            } else {
                errorCallback('Uses require, but no AMD loader is present');
            }
        } else {
            callback(config);
        }
    }

    ko.exportSymbol('components.register', ko.components.register);
    ko.exportSymbol('components.isRegistered', ko.components.isRegistered);
    ko.exportSymbol('components.unregister', ko.components.unregister);

    // Expose the default loader so that developers can directly ask it for configuration
    // or to resolve configuration
    ko.exportSymbol('components.defaultLoader', ko.components.defaultLoader);

    // By default, the default loader is the only registered component loader
    ko.components['loaders'].push(ko.components.defaultLoader);
})();
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react';
import {Button, ButtonGroup} from 'react-bootstrap';
import Select from "react-select";
import moment from 'moment';
import numeral from 'numeral';
import Immutable from 'immutable';
import * as Utils from '../utils';
import Api from "../../api";
import _ from 'underscore';
import jqueryui from 'jquery-ui';

export default class BookForm extends Component {

    static contextTypes = {
        history: React.PropTypes.object.isRequired
    }

	constructor(props) {
		super(props);

		this.state = {
			id: this.props.book.id,
			title: this.props.book.title,
			language: this.props.book.language_id,
			authors: this.mapAuthors(this.props.book.authors),
			pageCount: this.props.book.page_count,
			price: this.props.book.price,
			isRead: this.props.book.is_read,
			startedReading: moment(this.props.book.started_reading).format('DD.MM.YYYY'),
			finishedReading: moment(this.props.book.finished_reading).format('DD.MM.YYYY')
		}
	}

	mapAuthors(authors) {
		if (!authors) {
			return [];
		}

		let items = [];

		items = authors.map(author => {
			return {value: author.id, label: author.name};
		});

		return items;
	}

	searchAuthors(input, callback) {
		const {d} = this.props;
		Api.searchAuthors(input).then(authors => {

			let mapped = Immutable.List();

			authors.map(author => {
				mapped = mapped.push({
					value: author.id,
					label: author.name,
				})
			});

    		let selections = {options: mapped.toArray()};
			callback(null, selections);
		});
	}

	getOptions(input, callback) {
		var self = this;

	    setTimeout(() => {
	    	self.searchAuthors(input, callback);
	    }, 500);
	}

	renderOption(author) {
		return (

					{author.label}

		);
	}

	onAuthorChanged(selectioString, selectionObj) {
		this.setState({
			authors: Immutable.Seq(selectionObj).filter(x => x.value).toArray()
		});
	}

	processForm(e) {
		e.preventDefault();

		let formData = {
			id: this.state.id,
			title: this.state.title,
			authors: this.state.authors,
			language: this.state.language,
			pageCount: this.state.pageCount,
			price: this.state.price,
			isRead: this.state.isRead,
			startedReading: this.state.startedReading,
			finishedReading: this.state.finishedReading
		};

		alert(JSON.stringify(formData));

		const { handleSubmit} = this.props;

		//handleSubmit(formData);
	}

	handleChange(e, name, field) {
		let nextState = {}
		let value = null;

		if (e.target.type == "checkbox") {
			value = e.target.checked;
		} else {
			value = e.target.value;
		}

		this.setState({
			[e.target.name]: value
		});
	}

	handleStartedReadingChange(date) {
		this.setState({
			startedReading: date,
		});
	}

	handleFinishedReadingChange(date) {
		this.setState({
			finishedReading: date,
		});
	}

	componentDidMount() {
		let self = this;
		let datePickerConfig = {
			dateFormat: "dd.mm.yy"
		};

		$('#startedReading').datepicker(datePickerConfig).on('change', function(e) {
			self.handleStartedReadingChange($(this).val());
		});

		$('#finishedReading').datepicker(datePickerConfig).on('change', function(e) {
			self.handleFinishedReadingChange($(this).val());
		});

		if (this.state.startedReading) {
			$('#startedReading').datepicker("setDate", this.state.startedReading);
		}

		if (this.state.finishedReading) {
			$('#finishedReading').datepicker("setDate", this.state.finishedReading);
		}
	}

	shouldComponentUpdate_INACTIVE(nextProps, nextState) {
		return this.state.date != null && this.state.date != nextState.date;
	}

    render() {
    	const renderInput = (value, name, label) =>

			{label}

				<input type="text" className="form-control" name={name} id={name} value={value} onChange={::this.handleChange}/>

;

    	return (

New Book

				<form className="form-horizontal" onSubmit={::this.processForm}>
					{renderInput(this.state.title, 'title', 'Title')}

				    	Language

						    	<input type="radio" name="language" id="option1"  value="fi" autoComplete="off" checked={this.state.language=="fi"} onChange={::this.handleChange} />
						  		Finnish

						    	<input type="radio" name="language" id="option2" value="se" autoComplete="off" checked={this.state.language=="se"} onChange={::this.handleChange} />
						  		Swedish

						    	<input type="radio" name="language" id="option3"  value="en" autoComplete="off" checked={this.state.language=="en"} onChange={::this.handleChange} />
						  		English

				    	Authors

						<Select
							valueKey="value"
							labelKey="label"
							name="authors"
							multi={true}
							searchable={true}
							autoload={false}
							cacheAsyncResults={false}
							value={this.state.authors}
							asyncOptions={::this.getOptions}
							optionRenderer={this.renderOption}
							onChange={::this.onAuthorChanged}
						></Select>

					{renderInput(this.state.pageCount, 'pageCount', 'Page count')}

					{renderInput(this.state.price, 'price', 'Price')}

						Is read

							<input type="checkbox" className="form-control" name="isRead" id="isRead" checked={this.state.isRead} onChange={::this.handleChange}/>

						Started reading

							<input type="text"
								id="startedReading"
							    onChange={::this.handleStartedReadingChange}
							/>

						Finished reading

							<input type="text"
								id="finishedReading"
							    onChange={::this.handleFinishedReadingChange}
							/>

							<button className="btn btn-success" onClick={::this.processForm} >
								 Submit
							</button>

				</form>

    	);
  	}
}<commit_msg>- cleaned up props use with const {book} = this.props - now dates are set to null if no date is present<commit_after>import React, {Component, PropTypes} from 'react';
import {Button, ButtonGroup} from 'react-bootstrap';
import Select from "react-select";
import moment from 'moment';
import numeral from 'numeral';
import Immutable from 'immutable';
import * as Utils from '../utils';
import Api from "../../api";
import _ from 'underscore';
import jqueryui from 'jquery-ui';

export default class BookForm extends Component {

    static contextTypes = {
        history: React.PropTypes.object.isRequired
    }

	constructor(props) {
		super(props);

		const {book} = this.props;

		this.state = {
			id: book.id,
			title: book.title,
			language: book.language_id,
			authors: this.mapAuthors(book.authors),
			pageCount: book.page_count,
			price: book.price,
			isRead: book.is_read,
			startedReading: book.started_reading ? moment(book.started_reading).format('DD.MM.YYYY') : null,
			finishedReading: book.finished_reading ? moment(book.finished_reading).format('DD.MM.YYYY') : null
		}
	}

	mapAuthors(authors) {
		if (!authors) {
			return [];
		}

		let items = [];

		items = authors.map(author => {
			return {value: author.id, label: author.name};
		});

		return items;
	}

	searchAuthors(input, callback) {
		const {d} = this.props;
		Api.searchAuthors(input).then(authors => {

			let mapped = Immutable.List();

			authors.map(author => {
				mapped = mapped.push({
					value: author.id,
					label: author.name,
				})
			});

    		let selections = {options: mapped.toArray()};
			callback(null, selections);
		});
	}

	getOptions(input, callback) {
		var self = this;

	    setTimeout(() => {
	    	self.searchAuthors(input, callback);
	    }, 500);
	}

	renderOption(author) {
		return (

					{author.label}

		);
	}

	onAuthorChanged(selectioString, selectionObj) {
		this.setState({
			authors: Immutable.Seq(selectionObj).filter(x => x.value).toArray()
		});
	}

	processForm(e) {
		e.preventDefault();

		let formData = {
			id: this.state.id,
			title: this.state.title,
			authors: this.state.authors,
			language: this.state.language,
			pageCount: this.state.pageCount,
			price: this.state.price,
			isRead: this.state.isRead,
			startedReading: this.state.startedReading,
			finishedReading: this.state.finishedReading
		};

		alert(JSON.stringify(formData));

		const { handleSubmit} = this.props;

		//handleSubmit(formData);
	}

	handleChange(e, name, field) {
		let nextState = {}
		let value = null;

		if (e.target.type == "checkbox") {
			value = e.target.checked;
		} else {
			value = e.target.value;
		}

		this.setState({
			[e.target.name]: value
		});
	}

	handleStartedReadingChange(date) {
		this.setState({
			startedReading: date,
		});
	}

	handleFinishedReadingChange(date) {
		this.setState({
			finishedReading: date,
		});
	}

	componentDidMount() {
		let self = this;
		let datePickerConfig = {
			dateFormat: "dd.mm.yy"
		};

		$('#startedReading').datepicker(datePickerConfig).on('change', function(e) {
			self.handleStartedReadingChange($(this).val());
		});

		$('#finishedReading').datepicker(datePickerConfig).on('change', function(e) {
			self.handleFinishedReadingChange($(this).val());
		});

		if (this.state.startedReading) {
			$('#startedReading').datepicker("setDate", this.state.startedReading);
		}

		if (this.state.finishedReading) {
			$('#finishedReading').datepicker("setDate", this.state.finishedReading);
		}
	}

	shouldComponentUpdate_INACTIVE(nextProps, nextState) {
		return this.state.date != null && this.state.date != nextState.date;
	}

    render() {
    	const renderInput = (value, name, label) =>

			{label}

				<input type="text" className="form-control" name={name} id={name} value={value} onChange={::this.handleChange}/>

;

    	return (

New Book

				<form className="form-horizontal" onSubmit={::this.processForm}>
					{renderInput(this.state.title, 'title', 'Title')}

				    	Language

						    	<input type="radio" name="language" id="option1"  value="fi" autoComplete="off" checked={this.state.language=="fi"} onChange={::this.handleChange} />
						  		Finnish

						    	<input type="radio" name="language" id="option2" value="se" autoComplete="off" checked={this.state.language=="se"} onChange={::this.handleChange} />
						  		Swedish

						    	<input type="radio" name="language" id="option3"  value="en" autoComplete="off" checked={this.state.language=="en"} onChange={::this.handleChange} />
						  		English

				    	Authors

						<Select
							valueKey="value"
							labelKey="label"
							name="authors"
							multi={true}
							searchable={true}
							autoload={false}
							cacheAsyncResults={false}
							value={this.state.authors}
							asyncOptions={::this.getOptions}
							optionRenderer={this.renderOption}
							onChange={::this.onAuthorChanged}
						></Select>

					{renderInput(this.state.pageCount, 'pageCount', 'Page count')}

					{renderInput(this.state.price, 'price', 'Price')}

						Is read

							<input type="checkbox" className="form-control" name="isRead" id="isRead" checked={this.state.isRead} onChange={::this.handleChange}/>

						Started reading

							<input type="text"
								id="startedReading"
							    onChange={::this.handleStartedReadingChange}
							/>

						Finished reading

							<input type="text"
								id="finishedReading"
							    onChange={::this.handleFinishedReadingChange}
							/>

							<button className="btn btn-success" onClick={::this.processForm} >
								 Submit
							</button>

				</form>

    	);
  	}
}<|endoftext|>


<commit_before>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/**
 * Loader is responsible for parse embed parameters and decide which plugins should
 * be used in order to satisfy the playback.
 */

var BaseObject = require('../../base/base_object');
var _ = require('underscore');

/* Playback Plugins */
var HTML5VideoPlaybackPlugin = require('../../playbacks/html5_video');
var FlashVideoPlaybackPlugin = require('../../playbacks/flash_vod');
var HTML5AudioPlaybackPlugin = require('../../playbacks/html5_audio');
var HLSVideoPlaybackPlugin = require('../../playbacks/hls');

/* Container Plugins */
var SpinnerThreeBouncePlugin = require('../../plugins/spinner_three_bounce');
var StatsPlugin = require('../../plugins/stats');
var WaterMarkPlugin = require('../../plugins/watermark');
var PosterPlugin = require('../../plugins/poster');

/* Core Plugins */
var BackgroundButton = require('../../plugins/background_button');
var SeekTime = require('../../plugins/seek_time');

class Loader extends BaseObject {
  get displayPlugins() {
    return {};
  }

  constructor(options) {
    super(options);
    this.options = options
    this.playbackPlugins = [FlashVideoPlaybackPlugin, HTML5VideoPlaybackPlugin, HTML5AudioPlaybackPlugin, HLSVideoPlaybackPlugin]
    this.containerPlugins = [SpinnerThreeBouncePlugin, WaterMarkPlugin, PosterPlugin, StatsPlugin]
    this.globalPlugins = [BackgroundButton, SeekTime]
    if (this.options.displayPlugins && this.displayPlugins[this.options.displayType]) {
      this.globalPlugins.push(this.displayPlugins[this.options.displayType])
    }
    this.addExternalPlugins(options.plugins || [])
  }

  addExternalPlugins(plugins) {
    if(plugins.playback) {
      this.playbackPlugins = plugins.playback.concat(this.playbackPlugins)
    }
    if(plugins.container) {
      this.containerPlugins = plugins.container.concat(this.containerPlugins)
    }
    if(plugins.core) {
      this.globalPlugins = plugins.core.concat(this.globalPlugins)
    }
  }

  getPlugin(name) {
    var allPlugins = _.union(this.containerPlugins, this.playbackPlugins, this.globalPlugins)
    return _.find(allPlugins, function(plugin) { return plugin.prototype.name === name })
  }
}

module.exports = Loader;
<commit_msg>loader: remove outdated comment<commit_after>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var BaseObject = require('../../base/base_object');
var _ = require('underscore');

/* Playback Plugins */
var HTML5VideoPlaybackPlugin = require('../../playbacks/html5_video');
var FlashVideoPlaybackPlugin = require('../../playbacks/flash_vod');
var HTML5AudioPlaybackPlugin = require('../../playbacks/html5_audio');
var HLSVideoPlaybackPlugin = require('../../playbacks/hls');

/* Container Plugins */
var SpinnerThreeBouncePlugin = require('../../plugins/spinner_three_bounce');
var StatsPlugin = require('../../plugins/stats');
var WaterMarkPlugin = require('../../plugins/watermark');
var PosterPlugin = require('../../plugins/poster');

/* Core Plugins */
var BackgroundButton = require('../../plugins/background_button');
var SeekTime = require('../../plugins/seek_time');

class Loader extends BaseObject {
  get displayPlugins() {
    return {};
  }

  constructor(options) {
    super(options);
    this.options = options
    this.playbackPlugins = [FlashVideoPlaybackPlugin, HTML5VideoPlaybackPlugin, HTML5AudioPlaybackPlugin, HLSVideoPlaybackPlugin]
    this.containerPlugins = [SpinnerThreeBouncePlugin, WaterMarkPlugin, PosterPlugin, StatsPlugin]
    this.globalPlugins = [BackgroundButton, SeekTime]
    if (this.options.displayPlugins && this.displayPlugins[this.options.displayType]) {
      this.globalPlugins.push(this.displayPlugins[this.options.displayType])
    }
    this.addExternalPlugins(options.plugins || [])
  }

  addExternalPlugins(plugins) {
    if(plugins.playback) {
      this.playbackPlugins = plugins.playback.concat(this.playbackPlugins)
    }
    if(plugins.container) {
      this.containerPlugins = plugins.container.concat(this.containerPlugins)
    }
    if(plugins.core) {
      this.globalPlugins = plugins.core.concat(this.globalPlugins)
    }
  }

  getPlugin(name) {
    var allPlugins = _.union(this.containerPlugins, this.playbackPlugins, this.globalPlugins)
    return _.find(allPlugins, function(plugin) { return plugin.prototype.name === name })
  }
}

module.exports = Loader;
<|endoftext|>


<commit_before>var registerComponent = require('../core/component').registerComponent;
var THREE = require('../lib/three');

// To avoid recalculation at every mouse movement tick
var PI_2 = Math.PI / 2;

module.exports.Component = registerComponent('look-controls', {
  dependencies: ['position', 'rotation'],

  schema: {
    enabled: { default: true }
  },

  init: function () {
    this.previousPosition = new THREE.Vector3();
    this.deltaPosition = new THREE.Vector3();
    this.setupMouseControls();
    this.setupHMDControls();
    this.bindMethods();
  },

  update: function () {
    if (!this.data.enabled) { return; }
    this.controls.update();
    this.updateOrientation();
    this.updatePosition();
  },

  play: function () {
    this.previousPosition.set(0, 0, 0);
    this.addEventListeners();
  },

  pause: function () {
    this.removeEventListeners();
  },

  tick: function (t) {
    this.update();
  },

  remove: function () {
    this.pause();
  },

  bindMethods: function () {
    this.onMouseDown = this.onMouseDown.bind(this);
    this.onMouseMove = this.onMouseMove.bind(this);
    this.releaseMouse = this.releaseMouse.bind(this);
    this.onTouchStart = this.onTouchStart.bind(this);
    this.onTouchMove = this.onTouchMove.bind(this);
    this.onTouchEnd = this.onTouchEnd.bind(this);
  },

  setupMouseControls: function () {
    // The canvas where the scene is painted
    this.mouseDown = false;
    this.pitchObject = new THREE.Object3D();
    this.yawObject = new THREE.Object3D();
    this.yawObject.position.y = 10;
    this.yawObject.add(this.pitchObject);
  },

  setupHMDControls: function () {
    this.dolly = new THREE.Object3D();
    this.euler = new THREE.Euler();
    this.controls = new THREE.VRControls(this.dolly);
    this.zeroQuaternion = new THREE.Quaternion();
  },

  addEventListeners: function () {
    var canvasEl = this.el.sceneEl.canvas;

    // Mouse Events
    canvasEl.addEventListener('mousedown', this.onMouseDown, false);
    canvasEl.addEventListener('mousemove', this.onMouseMove, false);
    canvasEl.addEventListener('mouseup', this.releaseMouse, false);
    canvasEl.addEventListener('mouseout', this.releaseMouse, false);

    // Touch events
    canvasEl.addEventListener('touchstart', this.onTouchStart);
    canvasEl.addEventListener('touchmove', this.onTouchMove);
    canvasEl.addEventListener('touchend', this.onTouchEnd);
  },

  removeEventListeners: function () {
    var sceneEl = document.querySelector('a-scene');
    var canvasEl = sceneEl && sceneEl.canvas;
    if (!canvasEl) { return; }

    // Mouse Events
    canvasEl.removeEventListener('mousedown', this.onMouseDown);
    canvasEl.removeEventListener('mousemove', this.onMouseMove);
    canvasEl.removeEventListener('mouseup', this.releaseMouse);
    canvasEl.removeEventListener('mouseout', this.releaseMouse);

    // Touch events
    canvasEl.removeEventListener('touchstart', this.onTouchStart);
    canvasEl.removeEventListener('touchmove', this.onTouchMove);
    canvasEl.removeEventListener('touchend', this.onTouchEnd);
  },

  updateOrientation: (function () {
    var hmdEuler = new THREE.Euler();
    hmdEuler.order = 'YXZ';
    return function () {
      var pitchObject = this.pitchObject;
      var yawObject = this.yawObject;
      var hmdQuaternion = this.calculateHMDQuaternion();
      hmdEuler.setFromQuaternion(hmdQuaternion);
      this.el.setAttribute('rotation', {
        x: THREE.Math.radToDeg(hmdEuler.x) + THREE.Math.radToDeg(pitchObject.rotation.x),
        y: THREE.Math.radToDeg(hmdEuler.y) + THREE.Math.radToDeg(yawObject.rotation.y),
        z: THREE.Math.radToDeg(hmdEuler.z)
      });
    };
  })(),

  calculateHMDQuaternion: (function () {
    var hmdQuaternion = new THREE.Quaternion();
    return function () {
      var dolly = this.dolly;
      if (!this.zeroed && !dolly.quaternion.equals(this.zeroQuaternion)) {
        this.zeroOrientation();
        this.zeroed = true;
      }
      hmdQuaternion.copy(this.zeroQuaternion).multiply(dolly.quaternion);
      return hmdQuaternion;
    };
  })(),

  updatePosition: (function () {
    var position = new THREE.Vector3();
    var quaternion = new THREE.Quaternion();
    var scale = new THREE.Vector3();
    return function () {
      var el = this.el;
      var deltaPosition = this.calculateDeltaPosition();
      var currentPosition = el.getComputedAttribute('position');
      this.el.object3D.matrixWorld.decompose(position, quaternion, scale);
      deltaPosition.applyQuaternion(quaternion);
      el.setAttribute('position', {
        x: currentPosition.x + deltaPosition.x,
        y: currentPosition.y + deltaPosition.y,
        z: currentPosition.z + deltaPosition.z
      });
    };
  })(),

  calculateDeltaPosition: function () {
    var dolly = this.dolly;
    var deltaPosition = this.deltaPosition;
    var previousPosition = this.previousPosition;
    deltaPosition.copy(dolly.position);
    deltaPosition.sub(previousPosition);
    previousPosition.copy(dolly.position);
    return deltaPosition;
  },

  updateHMDQuaternion: (function () {
    var hmdQuaternion = new THREE.Quaternion();
    return function () {
      var dolly = this.dolly;
      this.controls.update();
      if (!this.zeroed && !dolly.quaternion.equals(this.zeroQuaternion)) {
        this.zeroOrientation();
        this.zeroed = true;
      }
      hmdQuaternion.copy(this.zeroQuaternion).multiply(dolly.quaternion);
      return hmdQuaternion;
    };
  })(),

  zeroOrientation: function () {
    var euler = new THREE.Euler();
    euler.setFromQuaternion(this.dolly.quaternion.clone().inverse());
    // Cancel out roll and pitch. We want to only reset yaw
    euler.z = 0;
    euler.x = 0;
    this.zeroQuaternion.setFromEuler(euler);
  },

  onMouseMove: function (event) {
    var pitchObject = this.pitchObject;
    var yawObject = this.yawObject;
    var previousMouseEvent = this.previousMouseEvent;

    if (!this.mouseDown || !this.data.enabled) { return; }

    var movementX = event.movementX || event.mozMovementX;
    var movementY = event.movementY || event.mozMovementY;

    if (movementX === undefined || movementY === undefined) {
      movementX = event.screenX - previousMouseEvent.screenX;
      movementY = event.screenY - previousMouseEvent.screenY;
    }
    this.previousMouseEvent = event;

    yawObject.rotation.y -= movementX * 0.002;
    pitchObject.rotation.x -= movementY * 0.002;
    pitchObject.rotation.x = Math.max(-PI_2, Math.min(PI_2, pitchObject.rotation.x));
  },

  onMouseDown: function (event) {
    this.mouseDown = true;
    this.previousMouseEvent = event;
  },

  releaseMouse: function () {
    this.mouseDown = false;
  },

  onTouchStart: function (e) {
    if (e.touches.length !== 1) { return; }
    this.touchStart = {
      x: e.touches[0].pageX,
      y: e.touches[0].pageY
    };
    this.touchStarted = true;
  },

  onTouchMove: function (e) {
    var deltaY;
    var yawObject = this.yawObject;
    if (!this.touchStarted) { return; }
    deltaY = 2 * Math.PI * (e.touches[0].pageX - this.touchStart.x) /
            this.el.sceneEl.canvas.clientWidth;
    // Limits touch orientaion to to yaw (y axis)
    yawObject.rotation.y -= deltaY * 0.5;
    this.touchStart = {
      x: e.touches[0].pageX,
      y: e.touches[0].pageY
    };
  },

  onTouchEnd: function () {
    this.touchStarted = false;
  }
});
<commit_msg>waits for canvas to be loaded<commit_after>var registerComponent = require('../core/component').registerComponent;
var THREE = require('../lib/three');

// To avoid recalculation at every mouse movement tick
var PI_2 = Math.PI / 2;

module.exports.Component = registerComponent('look-controls', {
  dependencies: ['position', 'rotation'],

  schema: {
    enabled: { default: true }
  },

  init: function () {
    this.previousPosition = new THREE.Vector3();
    this.deltaPosition = new THREE.Vector3();
    this.setupMouseControls();
    this.setupHMDControls();
    this.bindMethods();
  },

  update: function () {
    if (!this.data.enabled) { return; }
    this.controls.update();
    this.updateOrientation();
    this.updatePosition();
  },

  play: function () {
    this.previousPosition.set(0, 0, 0);
    this.addEventListeners();
  },

  pause: function () {
    this.removeEventListeners();
  },

  tick: function (t) {
    this.update();
  },

  remove: function () {
    this.pause();
  },

  bindMethods: function () {
    this.onMouseDown = this.onMouseDown.bind(this);
    this.onMouseMove = this.onMouseMove.bind(this);
    this.releaseMouse = this.releaseMouse.bind(this);
    this.onTouchStart = this.onTouchStart.bind(this);
    this.onTouchMove = this.onTouchMove.bind(this);
    this.onTouchEnd = this.onTouchEnd.bind(this);
  },

  setupMouseControls: function () {
    // The canvas where the scene is painted
    this.mouseDown = false;
    this.pitchObject = new THREE.Object3D();
    this.yawObject = new THREE.Object3D();
    this.yawObject.position.y = 10;
    this.yawObject.add(this.pitchObject);
  },

  setupHMDControls: function () {
    this.dolly = new THREE.Object3D();
    this.euler = new THREE.Euler();
    this.controls = new THREE.VRControls(this.dolly);
    this.zeroQuaternion = new THREE.Quaternion();
  },

  addEventListeners: function () {
    var sceneEl = this.el.sceneEl;
    var canvasEl = sceneEl.canvas;

    // listen for canvas to load.
    if (!canvasEl) {
      sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
      return;
    }

    // Mouse Events
    canvasEl.addEventListener('mousedown', this.onMouseDown, false);
    canvasEl.addEventListener('mousemove', this.onMouseMove, false);
    canvasEl.addEventListener('mouseup', this.releaseMouse, false);
    canvasEl.addEventListener('mouseout', this.releaseMouse, false);

    // Touch events
    canvasEl.addEventListener('touchstart', this.onTouchStart);
    canvasEl.addEventListener('touchmove', this.onTouchMove);
    canvasEl.addEventListener('touchend', this.onTouchEnd);
  },

  removeEventListeners: function () {
    var sceneEl = document.querySelector('a-scene');
    var canvasEl = sceneEl && sceneEl.canvas;
    if (!canvasEl) { return; }

    // Mouse Events
    canvasEl.removeEventListener('mousedown', this.onMouseDown);
    canvasEl.removeEventListener('mousemove', this.onMouseMove);
    canvasEl.removeEventListener('mouseup', this.releaseMouse);
    canvasEl.removeEventListener('mouseout', this.releaseMouse);

    // Touch events
    canvasEl.removeEventListener('touchstart', this.onTouchStart);
    canvasEl.removeEventListener('touchmove', this.onTouchMove);
    canvasEl.removeEventListener('touchend', this.onTouchEnd);
  },

  updateOrientation: (function () {
    var hmdEuler = new THREE.Euler();
    hmdEuler.order = 'YXZ';
    return function () {
      var pitchObject = this.pitchObject;
      var yawObject = this.yawObject;
      var hmdQuaternion = this.calculateHMDQuaternion();
      hmdEuler.setFromQuaternion(hmdQuaternion);
      this.el.setAttribute('rotation', {
        x: THREE.Math.radToDeg(hmdEuler.x) + THREE.Math.radToDeg(pitchObject.rotation.x),
        y: THREE.Math.radToDeg(hmdEuler.y) + THREE.Math.radToDeg(yawObject.rotation.y),
        z: THREE.Math.radToDeg(hmdEuler.z)
      });
    };
  })(),

  calculateHMDQuaternion: (function () {
    var hmdQuaternion = new THREE.Quaternion();
    return function () {
      var dolly = this.dolly;
      if (!this.zeroed && !dolly.quaternion.equals(this.zeroQuaternion)) {
        this.zeroOrientation();
        this.zeroed = true;
      }
      hmdQuaternion.copy(this.zeroQuaternion).multiply(dolly.quaternion);
      return hmdQuaternion;
    };
  })(),

  updatePosition: (function () {
    var position = new THREE.Vector3();
    var quaternion = new THREE.Quaternion();
    var scale = new THREE.Vector3();
    return function () {
      var el = this.el;
      var deltaPosition = this.calculateDeltaPosition();
      var currentPosition = el.getComputedAttribute('position');
      this.el.object3D.matrixWorld.decompose(position, quaternion, scale);
      deltaPosition.applyQuaternion(quaternion);
      el.setAttribute('position', {
        x: currentPosition.x + deltaPosition.x,
        y: currentPosition.y + deltaPosition.y,
        z: currentPosition.z + deltaPosition.z
      });
    };
  })(),

  calculateDeltaPosition: function () {
    var dolly = this.dolly;
    var deltaPosition = this.deltaPosition;
    var previousPosition = this.previousPosition;
    deltaPosition.copy(dolly.position);
    deltaPosition.sub(previousPosition);
    previousPosition.copy(dolly.position);
    return deltaPosition;
  },

  updateHMDQuaternion: (function () {
    var hmdQuaternion = new THREE.Quaternion();
    return function () {
      var dolly = this.dolly;
      this.controls.update();
      if (!this.zeroed && !dolly.quaternion.equals(this.zeroQuaternion)) {
        this.zeroOrientation();
        this.zeroed = true;
      }
      hmdQuaternion.copy(this.zeroQuaternion).multiply(dolly.quaternion);
      return hmdQuaternion;
    };
  })(),

  zeroOrientation: function () {
    var euler = new THREE.Euler();
    euler.setFromQuaternion(this.dolly.quaternion.clone().inverse());
    // Cancel out roll and pitch. We want to only reset yaw
    euler.z = 0;
    euler.x = 0;
    this.zeroQuaternion.setFromEuler(euler);
  },

  onMouseMove: function (event) {
    var pitchObject = this.pitchObject;
    var yawObject = this.yawObject;
    var previousMouseEvent = this.previousMouseEvent;

    if (!this.mouseDown || !this.data.enabled) { return; }

    var movementX = event.movementX || event.mozMovementX;
    var movementY = event.movementY || event.mozMovementY;

    if (movementX === undefined || movementY === undefined) {
      movementX = event.screenX - previousMouseEvent.screenX;
      movementY = event.screenY - previousMouseEvent.screenY;
    }
    this.previousMouseEvent = event;

    yawObject.rotation.y -= movementX * 0.002;
    pitchObject.rotation.x -= movementY * 0.002;
    pitchObject.rotation.x = Math.max(-PI_2, Math.min(PI_2, pitchObject.rotation.x));
  },

  onMouseDown: function (event) {
    this.mouseDown = true;
    this.previousMouseEvent = event;
  },

  releaseMouse: function () {
    this.mouseDown = false;
  },

  onTouchStart: function (e) {
    if (e.touches.length !== 1) { return; }
    this.touchStart = {
      x: e.touches[0].pageX,
      y: e.touches[0].pageY
    };
    this.touchStarted = true;
  },

  onTouchMove: function (e) {
    var deltaY;
    var yawObject = this.yawObject;
    if (!this.touchStarted) { return; }
    deltaY = 2 * Math.PI * (e.touches[0].pageX - this.touchStart.x) /
            this.el.sceneEl.canvas.clientWidth;
    // Limits touch orientaion to to yaw (y axis)
    yawObject.rotation.y -= deltaY * 0.5;
    this.touchStart = {
      x: e.touches[0].pageX,
      y: e.touches[0].pageY
    };
  },

  onTouchEnd: function () {
    this.touchStarted = false;
  }
});
<|endoftext|>


<commit_before>import { BaseComponent } from '../base/Base';
import Choices from 'choices.js';
import Formio from '../../formio';
import _each from 'lodash/each';
import _get from 'lodash/get';
import _debounce from 'lodash/debounce';
import _isEmpty from 'lodash/isEmpty';
import _isArray from 'lodash/isArray';
export class SelectComponent extends BaseComponent {
  constructor(component, options, data) {
    super(component, options, data);

    // Trigger an update.
    this.triggerUpdate = _debounce(this.updateItems.bind(this), 100);

    // If they wish to refresh on a value, then add that here.
    if (this.component.refreshOn) {
      this.on('change', (event) => {
        if (this.component.refreshOn === 'data') {
          this.refreshItems();
        }
        else if (event.changed.component.key === this.component.refreshOn) {
          this.refreshItems();
        }
      });
    }
  }

  refreshItems() {
    this.triggerUpdate();
    if (this.component.clearOnRefresh) {
      this.setValue(null);
    }
  }

  elementInfo() {
    let info = super.elementInfo();
    info.type = 'select';
    info.changeEvent = 'change';
    if (info.attr.placeholder) {
      delete info.attr.placeholder;
    }
    return info;
  }

  createWrapper() {
    return false;
  }

  itemTemplate(data) {
    return this.component.template ? this.interpolate(this.component.template, {item: data}) : data.label;
  }

  itemValue(data) {
    return this.component.valueProperty ? _get(data, this.component.valueProperty) : data;
  }

  setItems(items) {
    if (!this.choices) {
      return;
    }
    this.choices._clearChoices();

    // If they provided select values, then we need to get them instead.
    if (this.component.selectValues) {
      items = _get(items, this.component.selectValues);
    }

    // Iterate through each of the items.
    _each(items, (item) => {

      // Add the choice to the select list.
      this.choices._addChoice(this.itemValue(item), this.itemTemplate(item));
    });

    // If a value is provided, then select it.
    if (this.value) {
      this.setValue(this.value, true);
    }
    else {
      // If a default value is provided then select it.
      let defaultValue = this.defaultValue;
      if (defaultValue) {
        this.setValue(defaultValue);
      }
    }
  }

  loadItems(url, search, headers, options) {
    let query = (this.component.dataSrc === 'url') ? {} : {
      limit: 100,
      skip: 0
    };

    // Allow for url interpolation.
    url = this.interpolate(url, {
      data: this.data,
      formioBase: Formio.getBaseUrl()
    });

    // Add search capability.
    if (this.component.searchField && search) {
      query[this.component.searchField] = search;
    }

    // Add filter capability
    if (this.component.filter) {
      let filter = this.interpolate(this.component.filter, {data: this.data});
      url += ((url.indexOf('?') === -1) ? '?' : '&') + filter;
    }

    // If they wish to return only some fields.
    if (this.component.selectFields) {
      query.select = this.component.selectFields;
    }

    if (!_isEmpty(query)) {
      // Add the query string.
      url += '?' + Formio.serialize(query);
    }

    // Make the request.
    Formio.request(url, null, null, headers, options)
      .then((response) => this.setItems(response))
      .catch((err) => {
        this.events.emit('formio.error', err);
        console.warn('Unable to load resources for ' + this.component.key);
      })
  }

  /**
   * Get the request headers for this select dropdown.
   */
  get requestHeaders() {
    // Create the headers object.
    let headers = new Headers();

    // Add custom headers to the url.
    if (this.component.data && this.component.data.headers) {
      try {
        _each(this.component.data.headers, (header) => {
          if (header.key) {
            headers.set(header.key, header.value);
          }
        });
      }
      catch (err) {
        console.warn(err.message);
      }
    }

    return headers;
  }

  updateItems(searchInput) {
    if (!this.component.data) {
      console.warn('Select component ' + this.component.key + ' does not have data configuration.');
      return;
    }

    switch(this.component.dataSrc) {
      case 'values':
        this.component.valueProperty = 'value';
        this.setItems(this.component.data.values);
        break;
      case 'json':
        try {
          if (typeof this.component.data.json == 'string') {
            this.setItems(JSON.parse(this.component.data.json));
          }
          else {
            this.setItems(this.component.data.json);
          }
        }
        catch (err) {
          console.warn('Unable to parse JSON for ' + this.component.key);
        }
        break;
      case 'resource':
        let resourceUrl = this.options.formio ? this.options.formio.formsUrl : Formio.getProjectUrl() + '/form';
        resourceUrl += ('/' + this.component.data.resource + '/submission');

        try {
          this.loadItems(resourceUrl, searchInput, this.requestHeaders);
        }
        catch (err) {
          console.warn('Unable to load resources for ' + this.component.key);
        }
        break;
      case 'url':
        let url = this.component.data.url;
        if (url.substr(0, 1) === '/') {
          url = Formio.getBaseUrl() + this.component.data.url;
        }

        this.loadItems(url, searchInput, this.requestHeaders, {
          noToken: true
        });
        break;
    }
  }

  addInput(input, container) {
    super.addInput(input, container);
    if (this.component.multiple) {
      input.setAttribute('multiple', true);
    }
    var tabIndex = input.tabIndex;
    this.choices = new Choices(input, {
      removeItemButton: true,
      itemSelectText: '',
      classNames: {
        containerOuter: 'choices form-group formio-choices',
        containerInner: 'form-control'
      },
      shouldSort: false,
      position: 'bottom'
    });
    this.choices.itemList.tabIndex = tabIndex;

    // If a search field is provided, then add an event listener to update items on search.
    if (this.component.searchField) {
      input.addEventListener('search', (event) => this.triggerUpdate(event.detail.value));
    }

    // Create a pseudo-placeholder.
    if (
      this.component.placeholder &&
      !this.choices.placeholderElement
    ) {
      this.placeholder = this.ce('span', {
        class: 'formio-placeholder'
      }, [
        this.text(this.component.placeholder)
      ]);

      // Prepend the placeholder.
      this.choices.containerInner.insertBefore(this.placeholder, this.choices.containerInner.firstChild);
      input.addEventListener('addItem', () => {
        this.placeholder.style.visibility = 'hidden';
      }, false);
      input.addEventListener('removeItem', () => {
        let value = this.getValue();
        if (!value || !value.length) {
          this.placeholder.style.visibility = 'visible';
        }
      }, false);
    }

    if (this.disabled) {
      this.choices.disable();
    }
    this.triggerUpdate();
  }

  set disabled(disabled) {
    super.disabled = disabled;
    if (!this.choices) {
      return;
    }
    if (disabled) {
      this.choices.disable();
    }
    else {
      this.choices.enable();
    }
  }

  getValue() {
    if (this.value) {
      return this.value;
    }
    if (!this.choices) {
      return;
    }
    return this.choices.getValue(true);
  }

  setValue(value, flags) {
    flags = this.getFlags.apply(this, arguments);
    this.value = value;
    if (this.choices) {
      if (value && this.choices.store) {
        // Search for the choice.
        const choices = this.choices.store.getChoices();
        const foundChoice = choices.find((choice) => {
          // For resources we may have two different instances of the same resource
          // Unify them so we don't have two copies of the same thing in the dropdown
          // and so the correct resource gets selected in the first place
          if (choice.value._id && value._id && choice.value._id === value._id) {
            value = choice.value;
          }
          return choice.value === value;
        });

        // If it is not found, then add it.
        if (!foundChoice) {
          this.choices._addChoice(this.itemValue(value), this.itemTemplate(value));
        }
      }

      // Now set the value.
      if (value) {
        this.choices.setValueByChoice(_isArray(value) ? value : [value])
      }
      else {
        this.choices.removeActiveItems();
      }
    }
    this.updateValue(flags);
  }

  /**
   * Check if a component is eligible for multiple validation
   *
   * @return {boolean}
   */
  validateMultiple(value) {
    // Select component will contain one input when flagged as multiple.
    return false;
  }

  /**
   * Ouput this select dropdown as a string value.
   * @return {*}
   */
  asString(value) {
    value = value || this.getValue();
    value = (typeof value !== 'object') ? {label: value} : value;
    return this.itemTemplate(value);
  }

  destroy() {
    if (this.choices) {
      this.choices.destroy();
    }
  }
}
<commit_msg>Make the dropdown position configurable.<commit_after>import { BaseComponent } from '../base/Base';
import Choices from 'choices.js';
import Formio from '../../formio';
import _each from 'lodash/each';
import _get from 'lodash/get';
import _debounce from 'lodash/debounce';
import _isEmpty from 'lodash/isEmpty';
import _isArray from 'lodash/isArray';
export class SelectComponent extends BaseComponent {
  constructor(component, options, data) {
    super(component, options, data);

    // Trigger an update.
    this.triggerUpdate = _debounce(this.updateItems.bind(this), 100);

    // If they wish to refresh on a value, then add that here.
    if (this.component.refreshOn) {
      this.on('change', (event) => {
        if (this.component.refreshOn === 'data') {
          this.refreshItems();
        }
        else if (event.changed.component.key === this.component.refreshOn) {
          this.refreshItems();
        }
      });
    }
  }

  refreshItems() {
    this.triggerUpdate();
    if (this.component.clearOnRefresh) {
      this.setValue(null);
    }
  }

  elementInfo() {
    let info = super.elementInfo();
    info.type = 'select';
    info.changeEvent = 'change';
    if (info.attr.placeholder) {
      delete info.attr.placeholder;
    }
    return info;
  }

  createWrapper() {
    return false;
  }

  itemTemplate(data) {
    return this.component.template ? this.interpolate(this.component.template, {item: data}) : data.label;
  }

  itemValue(data) {
    return this.component.valueProperty ? _get(data, this.component.valueProperty) : data;
  }

  setItems(items) {
    if (!this.choices) {
      return;
    }
    this.choices._clearChoices();

    // If they provided select values, then we need to get them instead.
    if (this.component.selectValues) {
      items = _get(items, this.component.selectValues);
    }

    // Iterate through each of the items.
    _each(items, (item) => {

      // Add the choice to the select list.
      this.choices._addChoice(this.itemValue(item), this.itemTemplate(item));
    });

    // If a value is provided, then select it.
    if (this.value) {
      this.setValue(this.value, true);
    }
    else {
      // If a default value is provided then select it.
      let defaultValue = this.defaultValue;
      if (defaultValue) {
        this.setValue(defaultValue);
      }
    }
  }

  loadItems(url, search, headers, options) {
    let query = (this.component.dataSrc === 'url') ? {} : {
      limit: 100,
      skip: 0
    };

    // Allow for url interpolation.
    url = this.interpolate(url, {
      data: this.data,
      formioBase: Formio.getBaseUrl()
    });

    // Add search capability.
    if (this.component.searchField && search) {
      query[this.component.searchField] = search;
    }

    // Add filter capability
    if (this.component.filter) {
      let filter = this.interpolate(this.component.filter, {data: this.data});
      url += ((url.indexOf('?') === -1) ? '?' : '&') + filter;
    }

    // If they wish to return only some fields.
    if (this.component.selectFields) {
      query.select = this.component.selectFields;
    }

    if (!_isEmpty(query)) {
      // Add the query string.
      url += '?' + Formio.serialize(query);
    }

    // Make the request.
    Formio.request(url, null, null, headers, options)
      .then((response) => this.setItems(response))
      .catch((err) => {
        this.events.emit('formio.error', err);
        console.warn('Unable to load resources for ' + this.component.key);
      })
  }

  /**
   * Get the request headers for this select dropdown.
   */
  get requestHeaders() {
    // Create the headers object.
    let headers = new Headers();

    // Add custom headers to the url.
    if (this.component.data && this.component.data.headers) {
      try {
        _each(this.component.data.headers, (header) => {
          if (header.key) {
            headers.set(header.key, header.value);
          }
        });
      }
      catch (err) {
        console.warn(err.message);
      }
    }

    return headers;
  }

  updateItems(searchInput) {
    if (!this.component.data) {
      console.warn('Select component ' + this.component.key + ' does not have data configuration.');
      return;
    }

    switch(this.component.dataSrc) {
      case 'values':
        this.component.valueProperty = 'value';
        this.setItems(this.component.data.values);
        break;
      case 'json':
        try {
          if (typeof this.component.data.json == 'string') {
            this.setItems(JSON.parse(this.component.data.json));
          }
          else {
            this.setItems(this.component.data.json);
          }
        }
        catch (err) {
          console.warn('Unable to parse JSON for ' + this.component.key);
        }
        break;
      case 'resource':
        let resourceUrl = this.options.formio ? this.options.formio.formsUrl : Formio.getProjectUrl() + '/form';
        resourceUrl += ('/' + this.component.data.resource + '/submission');

        try {
          this.loadItems(resourceUrl, searchInput, this.requestHeaders);
        }
        catch (err) {
          console.warn('Unable to load resources for ' + this.component.key);
        }
        break;
      case 'url':
        let url = this.component.data.url;
        if (url.substr(0, 1) === '/') {
          url = Formio.getBaseUrl() + this.component.data.url;
        }

        this.loadItems(url, searchInput, this.requestHeaders, {
          noToken: true
        });
        break;
    }
  }

  addInput(input, container) {
    super.addInput(input, container);
    if (this.component.multiple) {
      input.setAttribute('multiple', true);
    }
    var tabIndex = input.tabIndex;
    this.choices = new Choices(input, {
      removeItemButton: true,
      itemSelectText: '',
      classNames: {
        containerOuter: 'choices form-group formio-choices',
        containerInner: 'form-control'
      },
      shouldSort: false,
      position: (this.component.dropdown || 'auto')
    });
    this.choices.itemList.tabIndex = tabIndex;

    // If a search field is provided, then add an event listener to update items on search.
    if (this.component.searchField) {
      input.addEventListener('search', (event) => this.triggerUpdate(event.detail.value));
    }

    // Create a pseudo-placeholder.
    if (
      this.component.placeholder &&
      !this.choices.placeholderElement
    ) {
      this.placeholder = this.ce('span', {
        class: 'formio-placeholder'
      }, [
        this.text(this.component.placeholder)
      ]);

      // Prepend the placeholder.
      this.choices.containerInner.insertBefore(this.placeholder, this.choices.containerInner.firstChild);
      input.addEventListener('addItem', () => {
        this.placeholder.style.visibility = 'hidden';
      }, false);
      input.addEventListener('removeItem', () => {
        let value = this.getValue();
        if (!value || !value.length) {
          this.placeholder.style.visibility = 'visible';
        }
      }, false);
    }

    if (this.disabled) {
      this.choices.disable();
    }
    this.triggerUpdate();
  }

  set disabled(disabled) {
    super.disabled = disabled;
    if (!this.choices) {
      return;
    }
    if (disabled) {
      this.choices.disable();
    }
    else {
      this.choices.enable();
    }
  }

  getValue() {
    if (this.value) {
      return this.value;
    }
    if (!this.choices) {
      return;
    }
    return this.choices.getValue(true);
  }

  setValue(value, flags) {
    flags = this.getFlags.apply(this, arguments);
    this.value = value;
    if (this.choices) {
      if (value && this.choices.store) {
        // Search for the choice.
        const choices = this.choices.store.getChoices();
        const foundChoice = choices.find((choice) => {
          // For resources we may have two different instances of the same resource
          // Unify them so we don't have two copies of the same thing in the dropdown
          // and so the correct resource gets selected in the first place
          if (choice.value._id && value._id && choice.value._id === value._id) {
            value = choice.value;
          }
          return choice.value === value;
        });

        // If it is not found, then add it.
        if (!foundChoice) {
          this.choices._addChoice(this.itemValue(value), this.itemTemplate(value));
        }
      }

      // Now set the value.
      if (value) {
        this.choices.setValueByChoice(_isArray(value) ? value : [value])
      }
      else {
        this.choices.removeActiveItems();
      }
    }
    this.updateValue(flags);
  }

  /**
   * Check if a component is eligible for multiple validation
   *
   * @return {boolean}
   */
  validateMultiple(value) {
    // Select component will contain one input when flagged as multiple.
    return false;
  }

  /**
   * Ouput this select dropdown as a string value.
   * @return {*}
   */
  asString(value) {
    value = value || this.getValue();
    value = (typeof value !== 'object') ? {label: value} : value;
    return this.itemTemplate(value);
  }

  destroy() {
    if (this.choices) {
      this.choices.destroy();
    }
  }
}
<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { Link, browserHistory } from 'react-router';
import { contextTypes } from '../../utils/decorators/withContext.js';

import * as loginActions from '../../actions/userActions.js';
import PlayerAutoComplete from '../players/PlayerAutoComplete.js';

import PlayerModel from '../../models/PlayerModel.js';

import TextField from 'material-ui/lib/text-field';
import RaisedButton from 'material-ui/lib/raised-button';
import Paper from 'material-ui/lib/paper';
import { util as storeUtil } from '../../store.js';
import { dispayFormat } from '../controls/Telephone.js';

@connect(state => {
  return {
    //config: state.config,
    user: state.user,
    //players: state.players,
    // clubs: state.clubs,
    // matches: state.matches,
    // teams: state.teams,
  };
}, loginActions)
export default class Login extends Component {
  static contextTypes = contextTypes;
  static propTypes = {
    // players: ImmutablePropTypes.listOf(PropTypes.instanceOf(PlayerModel).isRequired).isRequired,
    user: PropTypes.object.isRequired,
    logout: PropTypes.func.isRequired,
  }

  constructor() {
    super();
  }

  render() {
    const t = this.context.t;
    const paperStyle = {
      height: 310,
      width: 320,
      margin: 20,
      textAlign: 'center',
      display: 'inline-block',
    };

    var player = storeUtil.getPlayer(this.props.user.playerId);
    console.log(player);

    return (
      <Paper zDepth={1} style={paperStyle}>

{this.context.t('profile.headerText')}

{this.context.t('profile.loggedInText')} {this._reverseName((player || {name: 'Broken Code'}).name)}

{this.context.t('profile.email')} {player.contact.email}

{this.context.t('profile.gsm')} {dispayFormat(player.contact.mobile)}

        <RaisedButton label={t('nav.profilePhotos')}
          style={{marginTop: 15}} onClick={() => browserHistory.push(t.route('profilePhotos'))} />

        <RaisedButton label={t('nav.profilePhotosAvatar')}
          style={{marginTop: 15, marginLeft: 15}} onClick={() => browserHistory.push(t.route('profilePhotosAvatar'))} />

        <RaisedButton label={t('nav.changePassword')}
          style={{marginTop: 15}} onClick={() => browserHistory.push(t.route('changePassword'))} />

        <RaisedButton
          label={this.context.t('login.logoutButton')}
          secondary={true}
          style={{marginTop: 15}}
          onClick={::this._onLogout} />
      </Paper>
    );
  }

  _onLogout() {
    this.props.logout();
  }

  _reverseName(name) {
    var nameInParts = name.split(' ');
    if(nameInParts.length === 2)
    {
       return nameInParts[1] + ' ' + nameInParts[0];
    }
    if (nameInParts.length === 3)
    {
       return nameInParts[nameInParts.length - 1] + ' ' + nameInParts[nameInParts.length - 3]
       + ' ' + nameInParts[nameInParts.length - 2] ;
    }
    if (nameInParts.length === 4)
    {
       return nameInParts[nameInParts.length - 1] + ' ' + nameInParts[nameInParts.length - 4]
       + ' ' + nameInParts[nameInParts.length - 3] + ' ' + nameInParts[nameInParts.length - 2];
    }
  }
}<commit_msg>removed console.log<commit_after>import React, { PropTypes, Component } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { Link, browserHistory } from 'react-router';
import { contextTypes } from '../../utils/decorators/withContext.js';

import * as loginActions from '../../actions/userActions.js';
import PlayerAutoComplete from '../players/PlayerAutoComplete.js';

import PlayerModel from '../../models/PlayerModel.js';

import TextField from 'material-ui/lib/text-field';
import RaisedButton from 'material-ui/lib/raised-button';
import Paper from 'material-ui/lib/paper';
import { util as storeUtil } from '../../store.js';
import { dispayFormat } from '../controls/Telephone.js';

@connect(state => {
  return {
    //config: state.config,
    user: state.user,
    //players: state.players,
    // clubs: state.clubs,
    // matches: state.matches,
    // teams: state.teams,
  };
}, loginActions)
export default class Login extends Component {
  static contextTypes = contextTypes;
  static propTypes = {
    // players: ImmutablePropTypes.listOf(PropTypes.instanceOf(PlayerModel).isRequired).isRequired,
    user: PropTypes.object.isRequired,
    logout: PropTypes.func.isRequired,
  }

  constructor() {
    super();
  }

  render() {
    const t = this.context.t;
    const paperStyle = {
      height: 310,
      width: 320,
      margin: 20,
      textAlign: 'center',
      display: 'inline-block',
    };

    var player = storeUtil.getPlayer(this.props.user.playerId);

    return (
      <Paper zDepth={1} style={paperStyle}>

{this.context.t('profile.headerText')}

{this.context.t('profile.loggedInText')} {this._reverseName((player || {name: 'Broken Code'}).name)}

{this.context.t('profile.email')} {player.contact.email}

{this.context.t('profile.gsm')} {dispayFormat(player.contact.mobile)}

        <RaisedButton label={t('nav.profilePhotos')}
          style={{marginTop: 15}} onClick={() => browserHistory.push(t.route('profilePhotos'))} />

        <RaisedButton label={t('nav.profilePhotosAvatar')}
          style={{marginTop: 15, marginLeft: 15}} onClick={() => browserHistory.push(t.route('profilePhotosAvatar'))} />

        <RaisedButton label={t('nav.changePassword')}
          style={{marginTop: 15}} onClick={() => browserHistory.push(t.route('changePassword'))} />

        <RaisedButton
          label={this.context.t('login.logoutButton')}
          secondary={true}
          style={{marginTop: 15}}
          onClick={::this._onLogout} />
      </Paper>
    );
  }

  _onLogout() {
    this.props.logout();
  }

  _reverseName(name) {
    var nameInParts = name.split(' ');
    if(nameInParts.length === 2)
    {
       return nameInParts[1] + ' ' + nameInParts[0];
    }
    if (nameInParts.length === 3)
    {
       return nameInParts[nameInParts.length - 1] + ' ' + nameInParts[nameInParts.length - 3]
       + ' ' + nameInParts[nameInParts.length - 2] ;
    }
    if (nameInParts.length === 4)
    {
       return nameInParts[nameInParts.length - 1] + ' ' + nameInParts[nameInParts.length - 4]
       + ' ' + nameInParts[nameInParts.length - 3] + ' ' + nameInParts[nameInParts.length - 2];
    }
  }
}<|endoftext|>


<commit_before>(function() {
  // TODO(sissel): this code could use serious refactorlove.
  // TODO(sissel): Write something that will use history.pushState and fall back
  // to document.location.hash madness.

  if (typeof(window.console) === 'undefined') {
    window.console = {
      log: function() {
        // no-op if we don't have a console logger.
      }
    };
  }

  var logstash = {
    params: {
      offset: 0,
      count: 50,
    },

    search: function(query, options) {
      if (query == undefined || query == "") {
        return;
      }

      /* Default options */
      if (typeof(options) == 'undefined') {
        options = { graph: true };
      }

      /* Check for history.pushState */

      var display_query = query.replace("<", "<").replace(">", ">")
      $("#querystatus, #results h1").html("Loading query '" + display_query + "' (offset:" + logstash.params.offset + ", count:" + logstash.params.count + ") ")
      //console.log(logstash.params)
      logstash.params.q = query;

      /* Load the search results */
      $("#results").load("/api/search?format=html", logstash.params);

      if (options.graph != false) {
        /* Load the default histogram graph */
        logstash.params.interval = 3600000; /* 1 hour, default */
        logstash.histogram();
      } /* if options.graph != false */
      $("#query").val(logstash.params.q);
      logstash.hash = document.location.hash = escape(JSON.stringify(logstash.params));
    }, /* search */

    histogram: function(tries) {
      if (typeof(tries) == 'undefined') {
        /* GeoCities mode on the graph while waiting ...
         * This won't likely survive 1.0, but it's fun for now...
         * TODO(sissel): Replace with a 'loading' or something icon. */

        $("#visual").html("");

        tries = 4; /* default tries */
      }

      jQuery.getJSON("/api/histogram", logstash.params, function(histogram, text, jqxhr) {
        /* Load the data into the graph */
        var flot_data = [];
        // histogram is an array of { "key": ..., "count": ... }
        for (var i in histogram) {
          flot_data.push([parseInt(histogram[i]["key"]), histogram[i]["count"]])
        }

        /* Try to be intelligent about how we choose the histogram interval.
         * If there are too few data points, try a smaller interval.
         * If there are too many data points, try a larger interval.
         * Give up after a few tries and go with the last result.
         *
         * This queries the backend several times, but should be reasonably
         * speedy as this behaves roughly as a binary search. */

        /* I originally used '2' for this value for binary search-like tuning
         * on the graph interval, but it's not fast enough, so use 3 for now.
         */
        jumpsize = 3
        if (tries > 0 && logstash.params.interval > 1000) {
          if (flot_data.length < 6 && flot_data.length > 0) {
            console.log("Histogram bucket " + logstash.params.interval + " has only " + flot_data.length + " data points, trying smaller...");
            logstash.params.interval /= jumpsize;
          } else if (flot_data.length > 50) {
            console.log("Histogram bucket " + logstash.params.interval + " too many (" + flot_data.length + ") data points, trying larger interval...");
            logstash.params.interval *= jumpsize;
          }
          logstash.histogram(tries - 1);
          return;
        } else {
          /* Got a good interval, plot it */
          logstash.plot(flot_data, logstash.params.interval);
        }
      });
    },

    parse_params: function(href) {
      var query = href.replace(/^[^?]*\?/, "");
      if (query == href) {
        //console.log("No query params in link " + href);
        /* No query params */
        return {};
      }

      //console.log({ "query": query });
      var param_list = query.split("&");
      params = {};
      //console.log({ "Parsed params": params });
      for (var p in param_list) {
        var a = param_list[p].split("=");
        var key = a[0];
        var value = a[1];
        params[key] = unescape(value);
      }
      return params;
    },

    appendquery: function(query) {
      var newquery = $("#query").val();
      newquery += " " + query;
      logstash.search(newquery.trim());
    }, /* appendquery */

    plot: function(data, interval) {
      var target = $("#visual");
      target.css("display", "block");
      target.children().remove();
      console.log("Plotting data")
      console.log(data)

      var plot = $.plot(target,
        [ {  /* data */
            data: data,
            bars: {
              show: true,
              barWidth: interval,
            }
        } ],
        { /* options */
          xaxis: { mode: "time" },
          grid: { hoverable: true, clickable: true },
        }
      );

      /* Remove any previous bindings on this plot
       * If we don't do this, then all previously-registered events will fire
       * for this plot when clicked, and that's bad. */
      target.unbind("plotclick");
      target.bind("plotclick", function(e, pos, item) {
        if (item) {
          /* The following commented code should not be necessary anymore since I
           * believe I fixed the 'multiple plotclick' bug. */
          //var now = (new Date()).getTime();
          //if (now - logstash.last_plot_click < 50) {
            /* TODO(sissel): debug why this happens.
             * For some reason, sometimes clicking on the plot will result in
             * more than one click event being fired. I've seen up to 5 'clicks'
             * being fired for the same event. Very strange.
             *
             * This hack should block extra/buggy clicks from being processed.
             */
            //console.log("Skipping extra click on plot?")
            //return;
          //}
          //console.log("plotclick; last time: " + (now - logstash.last_plot_click));
          //logstash.last_plot_click = now;
          console.log(e, pos, item)
          start = logstash.ms_to_iso8601(item.datapoint[0]);
          end = logstash.ms_to_iso8601(item.datapoint[0] + interval);

          /* Clicking on the graph means a new search, means
           * we probably don't want to keep the old offset since
           * the search results will change. */
          logstash.params.offset = 0;
          logstash.appendquery("@timestamp:[" + start + " TO " + end + "]");
        }
      });
    }, /* plot */

    ms_to_iso8601: function(milliseconds) {
      /* From:
       * https://developer.mozilla.org/en/JavaScript/Reference/global_objects/date#Example.3a_ISO_8601_formatted_dates
       */
      var d = new Date(milliseconds);
      function pad(n){return n<10 ? '0'+n : n}
      return d.getUTCFullYear()+'-'
        + pad(d.getUTCMonth()+1)+'-'
        + pad(d.getUTCDate())+'T'
        + pad(d.getUTCHours())+':'
        + pad(d.getUTCMinutes())+':'
        + pad(d.getUTCSeconds())+'Z'
    },
  }; /* logstash */

  window.logstash = logstash;

  $().ready(function() {
    if (location.hash.length > 1) {
      try {
        logstash.params = JSON.parse(unescape(location.hash.substring(1)));
      } catch (e) {
        // Do nothing
      }
      logstash.search(logstash.params.q);
    } else {
      /* No hash. See if there's a query param. */
      var params = logstash.parse_params(location.href);
      //console.log(params)
      for (var p in params) {
        logstash.params[p] = params[p];
      }
      logstash.search(logstash.params.q)
    }

    $(window).hashchange(function() {
      /* Sometimes hashchange is called when we change the hash ourselves from
       * javascript. We don't want that. */
      if (logstash.hash === location.hash.substring(1)) {
        console.log("Ignoring superfluous hashchange")
        return;
      }
      logstash.params = JSON.parse(unescape(location.hash.substring(1)));
      console.log("hashchange");
      query = logstash.params.q
      if (query != $("#query").val()) {
        scroll(0, 0);
        logstash.search(query);
      }
    });

    $("a.pager, a.querychanger").live("click", function() {
      /* TODO(sissel): Allow 'control click' and 'middle click' to act normally */
      var href = $(this).attr("href");
      var params = logstash.parse_params(href);
      for (var p in params) {
        logstash.params[p] = params[p];
      }
      logstash.search(logstash.params.q, { graph: false })
      return false;
    });

    var result_row_selector = "table.results tr.event";
    $(result_row_selector).live("click", function() {
      var data = $("td.message", this).data("full");
      if (typeof(data) == "string") {
        data = JSON.parse(data);
      }

      /* Apply template to the dialog */
      var query = $("#query").val().replace(/^\s+|\s+$/g, "")
      var sanitize = function(str) {
        if (!/^".*"$/.test(str)) {
          str = '"' + str + '"';
        }
        return escape(str);
      };

      var template = $.template("inspector",
        "
" +
          "(${type}) ${field}:" +
          "{{each(idx, val) value}}" +
            "" +
              "${val}" +
            ", " +
          "{{/each}}" +
        "
");

      /* TODO(sissel): recurse through the data */
      var fields = new Array();
      for (var i in data["@fields"]) {
        var value = data["@fields"][i]
        if (/^[, ]*$/.test(value)) {
          continue; /* Skip empty data fields */
        }
        if (!(value instanceof Array)) {
          value = [value];
        }
        fields.push( { type: "field", field: i, value: value })
      }

      for (var i in data) {
        if (i == "@fields") continue;
        var value = data[i]
        if (!(value instanceof Array)) {
          value = [value];
        }

        if (i.charAt(0) == "@") { /* metadata */
          fields.push( { type: "metadata", field: i, value: value });
        } else { /* data */
          if (/^[, ]*$/.test(value)) {
            continue; /* Skip empty data fields */
          }
          fields.push( { type: "field", field: i, value: value })
        }
      }

      //for (var i in data) {
        //if (i == "_source") {
          //continue; /* already processed this one */
        //}
        //value = data[i]
        //if (!(value instanceof Array)) {
          //value = [value];
        //}
        //fields.push( { type: "metadata", field: i, value: value })
      //}

      fields.sort(function(a, b) {
        if (a.type+a.field  b.type+b.field) { return 1; }
        return 0;
      });

      $(result_row_selector).removeClass("selected")
      $(this).addClass("selected");
      var entry = this;
      $("#inspector li").remove()
      $("#inspector")
        .append($.tmpl("inspector", fields, { "sanitize": sanitize }))
        .dialog({
          width: 400,
          title: "Fields for this log" ,
          closeOnEscape: true,
          position: ["right", "top"],
        });
    });

    $("#inspector li a").live("click", function(ev) {
      var field = $(this).data("field");
      var value = $(this).data("value");
      var query = $("#query");
      var newcondition = unescape(field) + ":" + unescape(value);

      var newquery = query.val();
      if (ev.shiftKey) {
        // Shift-click will make a "and not" condition
        query.val(newquery + " -" + newcondition)
      } else {
        query.val(newquery + " " + newcondition)
      }
      logstash.search(query.val())
      return false;
    });

    $("#searchbutton").bind("click submit", function(ev) {
      var query = $("#query").val().replace(/^\s+|\s+$/g, "")
      /* Search now, we pressed the submit button */
      logstash.search(query)
      return false;
    });
  }); /* $().ready */
})(); /* function scoping */
<commit_msg>- put throbber on top of the graph instead of replacing the graph.<commit_after>(function() {
  // TODO(sissel): this code could use serious refactorlove.
  // TODO(sissel): Write something that will use history.pushState and fall back
  // to document.location.hash madness.

  if (typeof(window.console) === 'undefined') {
    window.console = {
      log: function() {
        // no-op if we don't have a console logger.
      }
    };
  }

  var logstash = {
    params: {
      offset: 0,
      count: 50,
    },

    search: function(query, options) {
      if (query == undefined || query == "") {
        return;
      }

      /* Default options */
      if (typeof(options) == 'undefined') {
        options = { graph: true };
      }

      /* Check for history.pushState */

      var display_query = query.replace("<", "<").replace(">", ">")
      $("#querystatus, #results h1").html("Loading query '" + display_query + "' (offset:" + logstash.params.offset + ", count:" + logstash.params.count + ") ")
      //console.log(logstash.params)
      logstash.params.q = query;

      /* Load the search results */
      $("#results").load("/api/search?format=html", logstash.params);

      if (options.graph != false) {
        /* Load the default histogram graph */
        logstash.params.interval = 3600000; /* 1 hour, default */
        logstash.histogram();
      } /* if options.graph != false */
      $("#query").val(logstash.params.q);
      logstash.hash = document.location.hash = escape(JSON.stringify(logstash.params));
    }, /* search */

    histogram: function(tries) {
      if (typeof(tries) == 'undefined') {
        /* GeoCities mode on the graph while waiting ...
         * This won't likely survive 1.0, but it's fun for now...
         * TODO(sissel): Replace with a 'loading' or something icon. */

        var throbber = $("")
        throbber.attr("id", "throbber")
        throbber.css("margin", "-100px");
        $("#visual #throbber").remove();
        $("#visual").append(throbber);

        tries = 4; /* default tries */
      }

      jQuery.getJSON("/api/histogram", logstash.params, function(histogram, text, jqxhr) {
        /* Load the data into the graph */
        var flot_data = [];
        // histogram is an array of { "key": ..., "count": ... }
        for (var i in histogram) {
          flot_data.push([parseInt(histogram[i]["key"]), histogram[i]["count"]])
        }

        /* Try to be intelligent about how we choose the histogram interval.
         * If there are too few data points, try a smaller interval.
         * If there are too many data points, try a larger interval.
         * Give up after a few tries and go with the last result.
         *
         * This queries the backend several times, but should be reasonably
         * speedy as this behaves roughly as a binary search. */

        /* I originally used '2' for this value for binary search-like tuning
         * on the graph interval, but it's not fast enough, so use 3 for now.
         */
        jumpsize = 3
        if (tries > 0 && logstash.params.interval > 1000) {
          if (flot_data.length < 6 && flot_data.length > 0) {
            console.log("Histogram bucket " + logstash.params.interval + " has only " + flot_data.length + " data points, trying smaller...");
            logstash.params.interval /= jumpsize;
          } else if (flot_data.length > 50) {
            console.log("Histogram bucket " + logstash.params.interval + " too many (" + flot_data.length + ") data points, trying larger interval...");
            logstash.params.interval *= jumpsize;
          }
          logstash.histogram(tries - 1);
          return;
        } else {
          /* Got a good interval, plot it */
          logstash.plot(flot_data, logstash.params.interval);
        }
      });
    },

    parse_params: function(href) {
      var query = href.replace(/^[^?]*\?/, "");
      if (query == href) {
        //console.log("No query params in link " + href);
        /* No query params */
        return {};
      }

      //console.log({ "query": query });
      var param_list = query.split("&");
      params = {};
      //console.log({ "Parsed params": params });
      for (var p in param_list) {
        var a = param_list[p].split("=");
        var key = a[0];
        var value = a[1];
        params[key] = unescape(value);
      }
      return params;
    },

    appendquery: function(query) {
      var newquery = $("#query").val();
      newquery += " " + query;
      logstash.search(newquery.trim());
    }, /* appendquery */

    plot: function(data, interval) {
      var target = $("#visual");
      target.css("display", "block");
      target.children().remove();
      console.log("Plotting data")
      console.log(data)

      var plot = $.plot(target,
        [ {  /* data */
            data: data,
            bars: {
              show: true,
              barWidth: interval,
            }
        } ],
        { /* options */
          xaxis: { mode: "time" },
          grid: { hoverable: true, clickable: true },
        }
      );

      /* Remove any previous bindings on this plot
       * If we don't do this, then all previously-registered events will fire
       * for this plot when clicked, and that's bad. */
      target.unbind("plotclick");
      target.bind("plotclick", function(e, pos, item) {
        if (item) {
          /* The following commented code should not be necessary anymore since I
           * believe I fixed the 'multiple plotclick' bug. */
          //var now = (new Date()).getTime();
          //if (now - logstash.last_plot_click < 50) {
            /* TODO(sissel): debug why this happens.
             * For some reason, sometimes clicking on the plot will result in
             * more than one click event being fired. I've seen up to 5 'clicks'
             * being fired for the same event. Very strange.
             *
             * This hack should block extra/buggy clicks from being processed.
             */
            //console.log("Skipping extra click on plot?")
            //return;
          //}
          //console.log("plotclick; last time: " + (now - logstash.last_plot_click));
          //logstash.last_plot_click = now;
          console.log(e, pos, item)
          start = logstash.ms_to_iso8601(item.datapoint[0]);
          end = logstash.ms_to_iso8601(item.datapoint[0] + interval);

          /* Clicking on the graph means a new search, means
           * we probably don't want to keep the old offset since
           * the search results will change. */
          logstash.params.offset = 0;
          logstash.appendquery("@timestamp:[" + start + " TO " + end + "]");
        }
      });
    }, /* plot */

    ms_to_iso8601: function(milliseconds) {
      /* From:
       * https://developer.mozilla.org/en/JavaScript/Reference/global_objects/date#Example.3a_ISO_8601_formatted_dates
       */
      var d = new Date(milliseconds);
      function pad(n){return n<10 ? '0'+n : n}
      return d.getUTCFullYear()+'-'
        + pad(d.getUTCMonth()+1)+'-'
        + pad(d.getUTCDate())+'T'
        + pad(d.getUTCHours())+':'
        + pad(d.getUTCMinutes())+':'
        + pad(d.getUTCSeconds())+'Z'
    },
  }; /* logstash */

  window.logstash = logstash;

  $().ready(function() {
    if (location.hash.length > 1) {
      try {
        logstash.params = JSON.parse(unescape(location.hash.substring(1)));
      } catch (e) {
        // Do nothing
      }
      logstash.search(logstash.params.q);
    } else {
      /* No hash. See if there's a query param. */
      var params = logstash.parse_params(location.href);
      //console.log(params)
      for (var p in params) {
        logstash.params[p] = params[p];
      }
      logstash.search(logstash.params.q)
    }

    $(window).hashchange(function() {
      /* Sometimes hashchange is called when we change the hash ourselves from
       * javascript. We don't want that. */
      if (logstash.hash === location.hash.substring(1)) {
        console.log("Ignoring superfluous hashchange")
        return;
      }
      logstash.params = JSON.parse(unescape(location.hash.substring(1)));
      console.log("hashchange");
      query = logstash.params.q
      if (query != $("#query").val()) {
        scroll(0, 0);
        logstash.search(query);
      }
    });

    $("a.pager, a.querychanger").live("click", function() {
      /* TODO(sissel): Allow 'control click' and 'middle click' to act normally */
      var href = $(this).attr("href");
      var params = logstash.parse_params(href);
      for (var p in params) {
        logstash.params[p] = params[p];
      }
      logstash.search(logstash.params.q, { graph: false })
      return false;
    });

    var result_row_selector = "table.results tr.event";
    $(result_row_selector).live("click", function() {
      var data = $("td.message", this).data("full");
      if (typeof(data) == "string") {
        data = JSON.parse(data);
      }

      /* Apply template to the dialog */
      var query = $("#query").val().replace(/^\s+|\s+$/g, "")
      var sanitize = function(str) {
        if (!/^".*"$/.test(str)) {
          str = '"' + str + '"';
        }
        return escape(str);
      };

      var template = $.template("inspector",
        "
" +
          "(${type}) ${field}:" +
          "{{each(idx, val) value}}" +
            "" +
              "${val}" +
            ", " +
          "{{/each}}" +
        "
");

      /* TODO(sissel): recurse through the data */
      var fields = new Array();
      for (var i in data["@fields"]) {
        var value = data["@fields"][i]
        if (/^[, ]*$/.test(value)) {
          continue; /* Skip empty data fields */
        }
        if (!(value instanceof Array)) {
          value = [value];
        }
        fields.push( { type: "field", field: i, value: value })
      }

      for (var i in data) {
        if (i == "@fields") continue;
        var value = data[i]
        if (!(value instanceof Array)) {
          value = [value];
        }

        if (i.charAt(0) == "@") { /* metadata */
          fields.push( { type: "metadata", field: i, value: value });
        } else { /* data */
          if (/^[, ]*$/.test(value)) {
            continue; /* Skip empty data fields */
          }
          fields.push( { type: "field", field: i, value: value })
        }
      }

      //for (var i in data) {
        //if (i == "_source") {
          //continue; /* already processed this one */
        //}
        //value = data[i]
        //if (!(value instanceof Array)) {
          //value = [value];
        //}
        //fields.push( { type: "metadata", field: i, value: value })
      //}

      fields.sort(function(a, b) {
        if (a.type+a.field  b.type+b.field) { return 1; }
        return 0;
      });

      $(result_row_selector).removeClass("selected")
      $(this).addClass("selected");
      var entry = this;
      $("#inspector li").remove()
      $("#inspector")
        .append($.tmpl("inspector", fields, { "sanitize": sanitize }))
        .dialog({
          width: 400,
          title: "Fields for this log" ,
          closeOnEscape: true,
          position: ["right", "top"],
        });
    });

    $("#inspector li a").live("click", function(ev) {
      var field = $(this).data("field");
      var value = $(this).data("value");
      var query = $("#query");
      var newcondition = unescape(field) + ":" + unescape(value);

      var newquery = query.val();
      if (ev.shiftKey) {
        // Shift-click will make a "and not" condition
        query.val(newquery + " -" + newcondition)
      } else {
        query.val(newquery + " " + newcondition)
      }
      logstash.search(query.val())
      return false;
    });

    $("#searchbutton").bind("click submit", function(ev) {
      var query = $("#query").val().replace(/^\s+|\s+$/g, "")
      /* Search now, we pressed the submit button */
      logstash.search(query)
      return false;
    });
  }); /* $().ready */
})(); /* function scoping */
<|endoftext|>


<commit_before>var path = require('path');

describe('findInstalled', function() {
    var findInstalled = require('../findInstalled');

    pit('must list default plugins for gitbook directory', function() {
        return findInstalled(path.resolve(__dirname, '../../../'))
        .then(function(plugins) {
            expect(plugins.size > 7).toBeTruthy();

            expect(plugins.has('fontsettings')).toBe(true);
            expect(plugins.has('search')).toBe(true);
        });
    });

});
<commit_msg>Fix lib/plugins/__tests__/findInstalled.js<commit_after>var path = require('path');

describe('findInstalled', function() {
    var findInstalled = require('../findInstalled');

    pit('must list default plugins for gitbook directory', function() {
        return findInstalled(path.resolve(__dirname, '../../../'))
        .then(function(plugins) {
            expect(plugins.size > 6).toBeTruthy();

            expect(plugins.has('fontsettings')).toBe(true);
            expect(plugins.has('search')).toBe(true);
        });
    });

});
<|endoftext|>