<commit_before>/*jslint browser: true*/

(function( vui ) {

	'use strict';

	// Check if the provided vui global is defined, otherwise try to require it if
	// we're in a CommonJS environment; otherwise we'll just fail out
	if( vui === undefined ) {
		if( typeof require === 'function' ) {
			vui = require('../../core');
		} else {
			throw new Error('load vui first');
		}
	}

	// Export the vui object if we're in a CommonJS environment.
	// It will already be on the window otherwise
	if( typeof module === 'object' && typeof module.exports === 'object' ) {
		module.exports = vui;
	}

	var classNames = {
			changed: 'vui-heading-collapsible-changed',
			target: 'vui-heading-collapsible-target',
			transition: 'vui-heading-collapsible-transition'
		};

	var transitionEnd = 'transitionend.vui webkitTransitionEnd.vui';

	var hasTransitions = 'transition' in document.body.style ||
		'webkitTransition' in document.body.style ||
		'MozTransition' in document.body.style;
	var finishTransition = function( evt, force ) {
			if( !hasTransitions || force ) {
				evt.originalEvent = { propertyName: 'height' };
				evt.data.me._handleTransitionEnd( evt );
			}
		};

	var $ = vui.$;

	$.widget( 'vui.vui_collapsibleSection', {

		_create: function() {

			var me = this;

			var targetInfo = this._getTargetInfo();
			if( targetInfo === null ) {
				return;
			}

			this._createAnchor();

			var evtData = {
					me: this,
					inProgress: false,
					isHover: false
				};

			this.element
				.on( 'vui-collapse', evtData, this._handleCollapse )
				.on( 'vui-expand', evtData, this._handleExpand )
				.on( 'click.vui', evtData, this._handleClick )
				.on( 'mouseover.vui', evtData, this._handleHover )
				.on( 'mouseout.vui', evtData, this._handleBlur );

			this.anchor
				.attr( 'aria-controls', targetInfo.id )
				.attr( 'aria-expanded', targetInfo.isVisible ? 'true' : 'false' )
				.on( 'focus', evtData, this._handleHover )
				.on( 'blur', evtData, this._handleBlur );

			this.icon
				.attr( 'class', targetInfo.isVisible ? 'vui-icon-collapse' : 'vui-icon-expand' );

			this.target
				.vui_changeTracker()
				.addClass( classNames.target )
				.attr( 'aria-hidden', targetInfo.isVisible ? 'false' : 'true' )
				.css( 'display', targetInfo.isVisible ? 'block' : 'none' )
				.on( transitionEnd, evtData, this._handleTransitionEnd )
				.on( 'vui-expand', evtData, this._handleExpand )
				.on( 'vui-change vui-restore', function() {
					me.anchor.toggleClass(
							classNames.changed,
							$( this ).vui_changeTracker('containsChanges')
						);
				} );

		},

		_destroy: function () {

			this.element
				.off( 'vui-collapse vui-expand click.vui mouseover.vui mouseout.vui vui-collapsibleSection-done' );

			this.icon.remove();
			this.anchor.contents().unwrap();

			this.target
				.removeClass( classNames.target + ' ' + classNames.transition )
				.off( transitionEnd + ' vui-expand vui-change vui-restore' )
				.removeAttr( 'aria-hidden' )
				.removeData( 'height' );

		},

		_createAnchor: function() {

			this.element.contents().wrapAll(
					''
				);
			this.anchor = this.element.find( 'a');

			this.icon = $( '' );
			this.anchor.append( this.icon );

		},

		_getTargetInfo: function() {

			var targetId = this.element.attr( 'data-target' );
			if( targetId === undefined ) {
				return null;
			}

			var target = document.getElementById( targetId );
			if( target === null ) {
				return null;
			}

			this.target = $( target );

			var targetInfo = {
					id: targetId,
					isVisible: this.target.is(":visible")
				};

			// initially hidden, we need to calculate height
			if( !targetInfo.isVisible ) {
				this.target.css(
						{
							position: 'absolute',
							visibility: 'hidden',
							display: 'block'
						}
					);
			}

			this.target.data( 'height', this.target.outerHeight( false ) );

			if( !targetInfo.isVisible ) {
				this.target.css(
						{
							position: 'static',
							visibility: 'visible',
							display: 'none'
						}
					);
			}

			return targetInfo;

		},

		_handleBlur: function( evt ) {

			evt.data.isHover = false;

			var isCollapsed = evt.data.me.target.attr( 'aria-hidden' ) == 'true';

			evt.data.me.icon.attr(
					'class',
					isCollapsed ? 'vui-icon-expand' : 'vui-icon-collapse'
				);

		},

		_handleClick: function( evt ) {

			var isCollapsed = evt.data.me.target.attr( 'aria-hidden' ) == 'true';
			$( this ).trigger( isCollapsed ? 'vui-expand' : 'vui-collapse' );

		},

		_handleCollapse: function( evt ) {

			if( evt.data.inProgress ) {
				return;
			}
			evt.data.inProgress = true;

			evt.data.me.icon.attr(
					'class',
					evt.data.isHover ? 'vui-icon-expand-h' : 'vui-icon-expand'
				);

			evt.data.me.anchor
				.attr( 'aria-expanded', false );

			var forceTransition = false;
			var targetIsVisible = evt.data.me.target.is(":visible");
			if( targetIsVisible ) {
				var targetHeight = evt.data.me.target.outerHeight( false );
				evt.data.me.target
					.data( 'height', targetHeight )
					.css( 'height', targetHeight + 'px' );
			} else {
				forceTransition = true;
			}

			setTimeout( function() {
					if( !evt.data ) {
						return;
					}
					evt.data.me.target
						.addClass( classNames.transition )
						.attr( 'aria-hidden', true )
						.css( 'height', '' );
					finishTransition( evt, forceTransition );
				}, 50 );

		},

		_handleExpand: function( evt ) {

			if( evt.data.inProgress ) {
				return;
			}
			evt.data.inProgress = true;

			evt.data.me.icon.attr(
					'class',
					evt.data.isHover ? 'vui-icon-collapse-h' : 'vui-icon-collapse'
				);

			evt.data.me.anchor
				.attr( 'aria-expanded', true );

			evt.data.me.target
				.css( 'display', 'block' );

			setTimeout( function() {
					if( !evt.data ) {
						return;
					}
					evt.data.me.target
						.addClass( classNames.transition )
						.attr( 'aria-hidden', false )
						.css( { 'height': evt.data.me.target.data('height') + 'px' } );
					evt.data.me._scroll( evt.data, true );
					finishTransition( evt );
				}, 50 );

		},

		_handleHover: function( evt ) {

			evt.data.isHover = true;

			var isCollapsed = evt.data.me.target.attr( 'aria-hidden' ) == 'true';
			evt.data.me.icon.attr(
					'class',
					isCollapsed ? 'vui-icon-expand-h' : 'vui-icon-collapse-h'
				);

		},

		_handleTransitionEnd: function( evt ) {

			if( !evt || !evt.originalEvent || evt.originalEvent.propertyName != 'height' ) {
				return;
			}

			evt.data.inProgress = false;

			evt.data.me.target.removeClass( classNames.transition );

			var isCollapsed = evt.data.me.target.attr( 'aria-hidden' ) == 'true';
			if( isCollapsed ) {
				evt.data.me.target.css( 'display', 'none' );
			} else {
				evt.data.me.target.css( 'height', '' );
				evt.data.me._scroll( evt.data, false );
			}

			evt.data.me.element.trigger( 'vui-collapsibleSection-done' );

		},

		_scroll: function( data, keepScrolling ) {

			var targetBottom = data.me.target.offset().top +
				data.me.target.height() + 50;
			var scrollTop = $( window ).scrollTop();
			var windowBottom = scrollTop + $( window ).height();

			var diff = targetBottom - windowBottom;
			if( diff > 0 ) {
				window.scrollTo( 0, scrollTop + diff );
			}

			if( keepScrolling ) {
				setTimeout(
						function() { data.me._scroll( data, true ); },
						10
					);
			}

		}

	} );

	vui.addClassInitializer(
			'vui-heading-collapsible',
			function( node ) {
				$( node ).vui_collapsibleSection();
			}
		);

} )( window.vui );<commit_msg>Fixing bug where if target had a height of 0, the transition-end event wasn't firing.<commit_after>/*jslint browser: true*/

(function( vui ) {

	'use strict';

	// Check if the provided vui global is defined, otherwise try to require it if
	// we're in a CommonJS environment; otherwise we'll just fail out
	if( vui === undefined ) {
		if( typeof require === 'function' ) {
			vui = require('../../core');
		} else {
			throw new Error('load vui first');
		}
	}

	// Export the vui object if we're in a CommonJS environment.
	// It will already be on the window otherwise
	if( typeof module === 'object' && typeof module.exports === 'object' ) {
		module.exports = vui;
	}

	var classNames = {
			changed: 'vui-heading-collapsible-changed',
			target: 'vui-heading-collapsible-target',
			transition: 'vui-heading-collapsible-transition'
		};

	var transitionEnd = 'transitionend.vui webkitTransitionEnd.vui';

	var hasTransitions = 'transition' in document.body.style ||
		'webkitTransition' in document.body.style ||
		'MozTransition' in document.body.style;
	var finishTransition = function( evt, force ) {
			if( !hasTransitions || force ) {
				evt.originalEvent = { propertyName: 'height' };
				evt.data.me._handleTransitionEnd( evt );
			}
		};

	var $ = vui.$;

	$.widget( 'vui.vui_collapsibleSection', {

		_create: function() {

			var me = this;

			var targetInfo = this._getTargetInfo();
			if( targetInfo === null ) {
				return;
			}

			this._createAnchor();

			var evtData = {
					me: this,
					inProgress: false,
					isHover: false
				};

			this.element
				.on( 'vui-collapse', evtData, this._handleCollapse )
				.on( 'vui-expand', evtData, this._handleExpand )
				.on( 'click.vui', evtData, this._handleClick )
				.on( 'mouseover.vui', evtData, this._handleHover )
				.on( 'mouseout.vui', evtData, this._handleBlur );

			this.anchor
				.attr( 'aria-controls', targetInfo.id )
				.attr( 'aria-expanded', targetInfo.isVisible ? 'true' : 'false' )
				.on( 'focus', evtData, this._handleHover )
				.on( 'blur', evtData, this._handleBlur );

			this.icon
				.attr( 'class', targetInfo.isVisible ? 'vui-icon-collapse' : 'vui-icon-expand' );

			this.target
				.vui_changeTracker()
				.addClass( classNames.target )
				.attr( 'aria-hidden', targetInfo.isVisible ? 'false' : 'true' )
				.css( 'display', targetInfo.isVisible ? 'block' : 'none' )
				.on( transitionEnd, evtData, this._handleTransitionEnd )
				.on( 'vui-expand', evtData, this._handleExpand )
				.on( 'vui-change vui-restore', function() {
					me.anchor.toggleClass(
							classNames.changed,
							$( this ).vui_changeTracker('containsChanges')
						);
				} );

		},

		_destroy: function () {

			this.element
				.off( 'vui-collapse vui-expand click.vui mouseover.vui mouseout.vui vui-collapsibleSection-done' );

			this.icon.remove();
			this.anchor.contents().unwrap();

			this.target
				.removeClass( classNames.target + ' ' + classNames.transition )
				.off( transitionEnd + ' vui-expand vui-change vui-restore' )
				.removeAttr( 'aria-hidden' )
				.removeData( 'height' );

		},

		_createAnchor: function() {

			this.element.contents().wrapAll(
					''
				);
			this.anchor = this.element.find( 'a');

			this.icon = $( '' );
			this.anchor.append( this.icon );

		},

		_getTargetInfo: function() {

			var targetId = this.element.attr( 'data-target' );
			if( targetId === undefined ) {
				return null;
			}

			var target = document.getElementById( targetId );
			if( target === null ) {
				return null;
			}

			this.target = $( target );

			var targetInfo = {
					id: targetId,
					isVisible: this.target.is(":visible")
				};

			// initially hidden, we need to calculate height
			if( !targetInfo.isVisible ) {
				this.target.css(
						{
							position: 'absolute',
							visibility: 'hidden',
							display: 'block'
						}
					);
			}

			this.target.data( 'height', this.target.outerHeight( false ) );

			if( !targetInfo.isVisible ) {
				this.target.css(
						{
							position: 'static',
							visibility: 'visible',
							display: 'none'
						}
					);
			}

			return targetInfo;

		},

		_handleBlur: function( evt ) {

			evt.data.isHover = false;

			var isCollapsed = evt.data.me.target.attr( 'aria-hidden' ) == 'true';

			evt.data.me.icon.attr(
					'class',
					isCollapsed ? 'vui-icon-expand' : 'vui-icon-collapse'
				);

		},

		_handleClick: function( evt ) {

			var isCollapsed = evt.data.me.target.attr( 'aria-hidden' ) == 'true';
			$( this ).trigger( isCollapsed ? 'vui-expand' : 'vui-collapse' );

		},

		_handleCollapse: function( evt ) {

			if( evt.data.inProgress ) {
				return;
			}
			evt.data.inProgress = true;

			evt.data.me.icon.attr(
					'class',
					evt.data.isHover ? 'vui-icon-expand-h' : 'vui-icon-expand'
				);

			evt.data.me.anchor
				.attr( 'aria-expanded', false );

			var forceTransition = false;
			var targetIsVisible = evt.data.me.target.is(":visible");
			if( targetIsVisible ) {
				var targetHeight = evt.data.me.target.outerHeight( false );
				evt.data.me.target
					.data( 'height', targetHeight )
					.css( 'height', targetHeight + 'px' );
			} else {
				forceTransition = true;
			}

			setTimeout( function() {
					if( !evt.data ) {
						return;
					}
					if( !forceTransition ) {
						forceTransition = evt.data.me.target.data('height') === 0;
					}
					evt.data.me.target
						.addClass( classNames.transition )
						.attr( 'aria-hidden', true )
						.css( 'height', '' );
					finishTransition( evt, forceTransition );
				}, 50 );

		},

		_handleExpand: function( evt ) {

			if( evt.data.inProgress ) {
				return;
			}
			evt.data.inProgress = true;

			evt.data.me.icon.attr(
					'class',
					evt.data.isHover ? 'vui-icon-collapse-h' : 'vui-icon-collapse'
				);

			evt.data.me.anchor
				.attr( 'aria-expanded', true );

			evt.data.me.target
				.css( 'display', 'block' );

			setTimeout( function() {
					if( !evt.data ) {
						return;
					}
					var force = evt.data.me.target.height() ==
						evt.data.me.target.data('height');
					evt.data.me.target
						.addClass( classNames.transition )
						.attr( 'aria-hidden', false )
						.css( { 'height': evt.data.me.target.data('height') + 'px' } );
					evt.data.me._scroll( evt.data, true );
					finishTransition( evt, force );
				}, 50 );

		},

		_handleHover: function( evt ) {

			evt.data.isHover = true;

			var isCollapsed = evt.data.me.target.attr( 'aria-hidden' ) == 'true';
			evt.data.me.icon.attr(
					'class',
					isCollapsed ? 'vui-icon-expand-h' : 'vui-icon-collapse-h'
				);

		},

		_handleTransitionEnd: function( evt ) {

			if( !evt || !evt.originalEvent || evt.originalEvent.propertyName != 'height' ) {
				return;
			}

			evt.data.inProgress = false;

			evt.data.me.target.removeClass( classNames.transition );

			var isCollapsed = evt.data.me.target.attr( 'aria-hidden' ) == 'true';
			if( isCollapsed ) {
				evt.data.me.target.css( 'display', 'none' );
			} else {
				evt.data.me.target.css( 'height', '' );
				evt.data.me._scroll( evt.data, false );
			}

			evt.data.me.element.trigger( 'vui-collapsibleSection-done' );

		},

		_scroll: function( data, keepScrolling ) {

			var targetBottom = data.me.target.offset().top +
				data.me.target.height() + 50;
			var scrollTop = $( window ).scrollTop();
			var windowBottom = scrollTop + $( window ).height();

			var diff = targetBottom - windowBottom;
			if( diff > 0 ) {
				window.scrollTo( 0, scrollTop + diff );
			}

			if( keepScrolling ) {
				setTimeout(
						function() { data.me._scroll( data, true ); },
						10
					);
			}

		}

	} );

	vui.addClassInitializer(
			'vui-heading-collapsible',
			function( node ) {
				$( node ).vui_collapsibleSection();
			}
		);

} )( window.vui );<|endoftext|>


<commit_before>'use strict';
const isBlank = require('is-blank');
const randomstring = require('randomstring');
const cli = require('../../bin/cli');
const prompt = require('./prompt');
const generate = require('./generate');
const command = cli.input[0];
const projectName = cli.input[1];

if (command === 'new') {
  if (isBlank(projectName)) {
    console.log('You need to specify a name for your new project.');
    process.exit(1);
  }

  prompt().then((options) => {
    console.log('Creating new BootBot Project...');
    options.project_name = projectName;
    options.verify_token = randomstring.generate();
    options.bot_port = 8007;
    options.bot_tunnel_subdomain = 'bootbot' + projectName + randomstring.generate({ length: 8, capitalization: 'lowercase'});
    options.bot_tunnel_subdomain = options.bot_tunnel_subdomain.slice(0, 60);
    generate(projectName, options);
    console.log(`
      Project created!

      Next steps:
      1) cd ${projectName}/
      2) npm install
      3) bootbot start

      For BootBot Documentation go to: https://github.com/Charca/bootbot

      Have fun!
    `);
    process.exit(0);
  });
}
<commit_msg>Normalized name for localtunnel subdomain<commit_after>'use strict';
const isBlank = require('is-blank');
const randomstring = require('randomstring');
const cli = require('../../bin/cli');
const prompt = require('./prompt');
const generate = require('./generate');
const command = cli.input[0];
const projectName = cli.input[1];

if (command === 'new') {
  if (isBlank(projectName)) {
    console.log('You need to specify a name for your new project.');
    process.exit(1);
  }

  prompt().then((options) => {
    console.log('Creating new BootBot Project...');
    options.project_name = projectName;
    options.verify_token = randomstring.generate();
    options.bot_port = 8007;
    options.bot_tunnel_subdomain = 'bootbot' +
      projectName.replace(/[^a-z0-9]/gi,'').toLowerCase() +
      randomstring.generate({ length: 8, capitalization: 'lowercase'});
    options.bot_tunnel_subdomain = options.bot_tunnel_subdomain.slice(0, 60);
    generate(projectName, options);
    console.log(`
      Project created!

      Next steps:
      1) cd ${projectName}/
      2) npm install
      3) bootbot start

      For BootBot Documentation go to: https://github.com/Charca/bootbot

      Have fun!
    `);
    process.exit(0);
  });
}
<|endoftext|>


<commit_before>/* global Handlebars */

/*
 * jQuery-handlebars
 * https://github.com/softwarespot/jquery-handlebars
 * Author: softwarespot
 * Licensed under the MIT license
 * Version: 1.1.3
 */
; // jshint ignore:line
(function ($, undefined) {

    // Initial idea by: http://blog.teamtreehouse.com/handlebars-js-part-3-tips-and-tricks

    // Plugin Logic

    $.fn.extend({

        // The parameter data doubles up as options when the action is either 'clear' or 'remove' i.e. options param will be ignored
        handlebars: function (action, template, dataOrOptions, options) {

            // jQuery object reference for this. Only select the first selector of the jQuery collection
            var $this = $(this).first();

            // The following was taken from sizzle.js, URL: https://github.com/jquery/sizzle/blob/master/dist/sizzle.js

            // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
            var identifier = '(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+';

            // Create a regular expression object to test valid id fragments
            var reIdentifier = new RegExp('^#' + identifier + '$');

            // Store whether the template is valid or invalid
            var isTemplateString = isString(template) && reIdentifier.test(template);

            // console.log('jQuery-handlebars: Is a template? [%s, %s]', template, isTemplateString);

            // These actions don't require any pre-processing
            if (isString(action)) {

                // If a 'get' action is provided, then get the template(s)
                // The template string if not defined will return all template(s)
                if (/^FIND|GET$/i.test(action)) {

                    // The include parameter will be true, if the template is a string and valid anchor
                    return getTemplate($this, template, isTemplateString);

                    // If a 'compiled' action is provided, then return the compiled object literal
                } else if (/^COMPILED|STORE$/i.test(action)) {

                    // Shallow copy the compiled store, otherwise returning compiled would provide a reference and allow the
                    // end user to manipulate the internal store. Not a good idea if you ask me!
                    return $.extend({}, _compiled);

                }

            } else {

                // An invalid action was passed. It must be a string with a length greater than zero to be
                // considered valid
                return this;

            }

            // Set our options from the defaults, overriding with the
            // parameter we pass into this function
            options = $.extend({}, $.fn.handlebars.options, options);

            // START: Sanitize the options

            // Check if the option type is a string and valid
            if (isString(options.type) && /^APPEND|COMPILED|HTML|RAW$/i.test(options.type)) {

                // Set to uppercase
                options.type = options.type.toUpperCase();

            } else {

                // Otherwise default to 'append' i.e. if null or simply invalid
                options.type = Type.APPEND;

            }

            // If the removal type is a string and valid, then set to uppercase
            if (isString(options.remove_type) && /^ALL|NONE|SAME$/i.test(options.remove_type)) {

                // Set to uppercase
                options.remove_type = options.remove_type.toUpperCase();

            } else {

                // Otherwise default to 'none' i.e. if null or simply is invalid
                options.remove_type = Remove.NONE;

            }

            // END: Sanitize the options

            // If a 'clear' action is provided, then remove the template from the content element
            if (/^CLEAR|EMPTY|REMOVE$/i.test(action)) {

                // Extend the options again, as the dataOrOptions acts as an alias for options when a removal action is
                // specified
                options = $.extend(options, dataOrOptions);

                // If the options parameter is not the 'same' and a template string exists, the
                // temporarily override
                if (isTemplateString && options.remove_type !== Remove.SAME) {

                    options.remove_type = Remove.SAME;

                } else {

                    // Otherwise use 'all'
                    options.remove_type = Remove.ALL;

                }

                // If the template is not defined, then set as null before removing
                if (!isTemplateString) {

                    template = null;

                }

                return removeTemplate(this, $this, template, options);

            }

            // Assume it's an addition i.e. 'add'

            // The data object literal must contain data
            if (isBoolean(options.validate) && options.validate && $.isEmptyObject(dataOrOptions)) {

                // console.log('jQuery-handlebars: Data was not passed to the plugin or is simply an empty plain object');
                return this;

            }

            // jQuery object reference
            var $selector = null;

            // If a valid string template is passed then check if it's compiled or get the jQuery object instead
            if (isTemplateString) {

                // If compiled already then no need to re-compile
                if ($.isFunction(_compiled[template])) {

                    // console.log('jQuery-handlebars: %s has already been compiled', template);

                    // Return to continue chaining
                    return setTemplate(this, $this, template, dataOrOptions, options);

                }

                // Not compiled

                // Get the first selection only
                $selector = $(template).first();

                // If the selector doesn't exist for whatever reason, then set to null
                if ($selector.length === 0) {

                    $selector = null;

                }

                // If a valid jQuery selector object
            } else if (template instanceof $ && $.isNumeric(template.length) && template.length > 0) {

                // Get the first selection only
                $selector = template.first();

                // Get the selector name for using with the compiled object literal
                // If the selector property doesn't exist, then this will be set to null
                template = $selector.selector !== undefined ? $selector.selector : null;

                // If compiled already then no need to re-compile
                if ($.isFunction(_compiled[template])) {

                    // console.log('jQuery-handlebars: %s has already been compiled', template);

                    // Return to continue chaining
                    return setTemplate(this, $this, template, dataOrOptions, options);

                }
            }

            // If an invalid selector, then return this to continue chaining
            if ($selector === null || template === null || $selector.length === 0) {

                // console.log('jQuery-handlebars: Template cannot be found');
                return this;

            }

            // Get the HTML of the template only
            var html = $selector.html();

            // Store the compiled template in the compiled store
            _compiled[template] = Handlebars.compile(html);

            // Return to continue chaining
            return setTemplate(this, $this, template, dataOrOptions, options);
        }

    });

    // Constants

    // The data-* attribute to distinguish a jQuery-handlebars template
    var DATA_ATTR = 'data-jquery-handlebars';

    // Removal constants. Who enjoys magic values?
    var Remove = {
        ALL: 'ALL',
        NONE: 'NONE',
        SAME: 'SAME'
    };

    // Type constants. Who enjoys magic values?
    var Type = {
        APPEND: 'APPEND',
        COMPILED: 'COMPILED',
        HTML: 'HTML',
        RAW: 'RAW'
    };

    // Fields (Private)

    // Store the compiled template(s) using the template string as the identifier i.e. key
    var _compiled = {};

    // Methods (Private)

    // Check if value is a boolean datatype
    function isBoolean(value) {

        return $.type(value) === 'boolean';

    }

    // Check if a value is a string datatype with a length greater than zero when whitespace is stripped
    function isString(value) {

        return $.type(value) === 'string' && value.trim().length > 0;

    }

    // Get the template in the content selector
    // include refers to whether to include the template in the selection
    function getTemplate($this, template, include) {

        // Get the divs with the template data attribute and optional specified template string e.g. #some-template
        var templateFind = (include && template ? '="' + template + '"' : '');
        return $this.find('div[' + DATA_ATTR + templateFind + ']');

    }

    // Remove the specified template from the content selector. If a template is not provided
    // then all template(s) that are contained within the content selector will be removed
    function removeTemplate(_this, $this, template, options) {

        // If the option has been passed to remove 'none', then respect this choice or if the template is null but the option is set to 'same'
        if (options.remove_type === Remove.NONE || (template === null && options.remove_type === Remove.SAME)) {

            // console.log('jQuery-handlebars: Template(s) were not removed [%s]', options.remove_type);

            // Return this to maintain chaining if there is nothing to remove
            return _this;

        }

        // The include parameter will be set to true, when specifically looking for the provided template string
        var filtered = getTemplate($this, template, options.remove_type === Remove.SAME);
        if (filtered.length === 0) {

            // console.log('jQuery-handlebars: Unable to find any template(s) for removal [%s]', options.remove_type);

            // Return this to maintain chaining if there is nothing to remove
            return _this;

        }

        // Remove filtered nodes from the DOM
        filtered.remove();

        // console.log('jQuery-handlebars: Removing template(s) [%s]', options.remove_type);

        // Remove the template from the compiled store, if the option is true
        if (isBoolean(options.delete_compiled) && options.delete_compiled) {

            // Iterate through the filtered collection and remove the template string from the compiled store
            $.each(filtered, function (index, element) {

                // Get the data attribute for the template string if it's not null or has already been removed
                // var attribute = $(element).attr(DATA_ATTR);
                var attribute = element.getAttribute(DATA_ATTR); // Returns null or '' on error
                if (!attribute && _compiled[attribute] !== undefined) {

                    // Set to undefined to mimic deletion of the template. Using delete is not really required
                    _compiled[attribute] = undefined;

                    // console.log('jQuery-handlebars: Removing compiled template [%s]', attribute);

                }

            });

            // console.log('jQuery-handlebars: Removed the template from the compiled store [%s]', template);

        }

    }

    // Set the specified template to the content selector
    function setTemplate(_this, $this, template, data, options) {

        // Override deleting from the compiled stored before passing to removeTemplate
        options.delete_compiled = false;

        // Remove template(s)
        removeTemplate(_this, $this, template, options);

        // If set to not refill and template node(s) exist, then return this
        if (isBoolean(options.refill) && !options.refill) {

            // Get the template(s) after potential removal. The parameter include has been set to true, as we are checking
            // if only the same template(s) exists (perhaps an option could/should be created)
            var filtered = getTemplate($this, template, true);
            if (filtered.length > 0) {

                // console.log('jQuery-handlebars: Refill has been set to false and the content element contains template(s) [%s]', options.remove_type);

                return _this;

            }

        }

        // Store the parsed template with data
        var parsedTemplate = _compiled[template](data);

        // Remove the compiled template from the store if specified
        if (isBoolean(options.store_compiled) && !options.store_compiled) {

            // Set to undefined to mimic deletion of the template. Using delete is not really required
            _compiled[template] = undefined;

            // console.log('jQuery-handlebars: Not stored in compiled storage [%s]', options.store_compiled);

        }

        // Append to the content element by checking the type. Default is 'append'
        switch (options.type) {
            case Type.COMPILED:
            case Type.RAW:

                // console.log('jQuery-handlebars: Returning raw HTML', template);

                // Return the compiled template
                return parsedTemplate;

            case Type.HTML:

                // console.log('jQuery-handlebars: Returning as HTML', template);

                // Return as HTML data
                return $(parsedTemplate);

            default:

                // Create a div element with the template appended to it
                // This contains a data-* attribute called data-jquery-handlebars for easy association
                // that it's a Handlebarjs template
                var $div = $('<div/>')
                    .attr(DATA_ATTR, template)
                    .append(parsedTemplate);

                // Append the div to content element
                $this.append($div);

                // console.log('jQuery-handlebars: Appending template to the content element [%s]', template);
                break;

        }

        return _this;
    }

    // Defaults

    $.fn.handlebars.options = {
        // Delete the template from the compiled store when a removal action is specified. Accepts true (default) or false
        delete_compiled: true,

        // Store a compiled template. This improves efficiency if using the same template continuously. Accepts true (default) or false
        store_compiled: true,

        // Allow the addition of multiple template(s) inside a content element. Accepts true (default) or false
        refill: true,

        // Remove pre-existing compiled templates from the specified content element when adding/appending a template
        //
        // The following options are:
        //      'none' (default): Don't remove any template(s)
        //      'all': Remove all valid template(s) from the content element
        //      'same': Remove only those template(s) that match the provided template string
        remove_type: Remove.NONE,

        // How to output the compiled template to the specified content element
        //
        // The following options are:
        //      'append' (default): Append to the content element
        //      'html': Return a compiled template as HTML
        //      'compiled'/'raw': Return a compiled template
        type: Type.APPEND,

        // Check whether the data passed to the plugin is empty
        validate: true
    };

})(jQuery);
<commit_msg>Removed comments and whitespace<commit_after>/* global Handlebars */

/*
 * jQuery-handlebars
 * https://github.com/softwarespot/jquery-handlebars
 * Author: softwarespot
 * Licensed under the MIT license
 * Version: 1.1.3
 */
; // jshint ignore:line
(function ($, undefined) {

    // Initial idea by: http://blog.teamtreehouse.com/handlebars-js-part-3-tips-and-tricks

    // Plugin Logic

    $.fn.extend({
        // The parameter data doubles up as options when the action is either 'clear' or 'remove' i.e. options param will be ignored
        handlebars: function (action, template, dataOrOptions, options) {
            // jQuery object reference for this. Only select the first selector of the jQuery collection
            var $this = $(this).first();

            // The following was taken from sizzle.js, URL: https://github.com/jquery/sizzle/blob/master/dist/sizzle.js

            // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
            var identifier = '(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+';

            // Create a regular expression object to test valid id fragments
            var reIdentifier = new RegExp('^#' + identifier + '$');

            // Store whether the template is valid or invalid
            var isTemplateString = isString(template) && reIdentifier.test(template);

            // These actions don't require any pre-processing
            if (isString(action)) {
                // If a 'get' action is provided, then get the template(s)
                // The template string if not defined will return all template(s)
                if (/^FIND|GET$/i.test(action)) {
                    // The include parameter will be true, if the template is a string and valid anchor
                    return getTemplate($this, template, isTemplateString);
                    // If a 'compiled' action is provided, then return the compiled object literal
                } else if (/^COMPILED|STORE$/i.test(action)) {
                    // Shallow copy the compiled store, otherwise returning compiled would provide a reference and allow the
                    // end user to manipulate the internal store. Not a good idea if you ask me!
                    return $.extend({}, _compiled);
                }
            } else {
                // An invalid action was passed. It must be a string with a length greater than zero to be
                // considered valid
                return this;
            }

            // Set our options from the defaults, overriding with the
            // parameter we pass into this function
            options = $.extend({}, $.fn.handlebars.options, options);

            // START: Sanitize the options

            // Check if the option type is a string and valid
            if (isString(options.type) && /^APPEND|COMPILED|HTML|RAW$/i.test(options.type)) {
                // Set to uppercase
                options.type = options.type.toUpperCase();
            } else {
                // Otherwise default to 'append' i.e. if null or simply invalid
                options.type = Type.APPEND;
            }

            // If the removal type is a string and valid, then set to uppercase
            if (isString(options.remove_type) && /^ALL|NONE|SAME$/i.test(options.remove_type)) {
                // Set to uppercase
                options.remove_type = options.remove_type.toUpperCase();
            } else {
                // Otherwise default to 'none' i.e. if null or simply is invalid
                options.remove_type = Remove.NONE;
            }

            // END: Sanitize the options

            // If a 'clear' action is provided, then remove the template from the content element
            if (/^CLEAR|EMPTY|REMOVE$/i.test(action)) {
                // Extend the options again, as the dataOrOptions acts as an alias for options when a removal action is
                // specified
                options = $.extend(options, dataOrOptions);

                // If the options parameter is not the 'same' and a template string exists, the
                // temporarily override
                if (isTemplateString && options.remove_type !== Remove.SAME) {
                    options.remove_type = Remove.SAME;
                } else {
                    // Otherwise use 'all'
                    options.remove_type = Remove.ALL;
                }

                // If the template is not defined, then set as null before removing
                if (!isTemplateString) {
                    template = null;
                }

                return removeTemplate(this, $this, template, options);
            }

            // Assume it's an addition i.e. 'add'

            // The data object literal must contain data
            if (isBoolean(options.validate) && options.validate && $.isEmptyObject(dataOrOptions)) {
                return this;
            }

            // jQuery object reference
            var $selector = null;

            // If a valid string template is passed then check if it's compiled or get the jQuery object instead
            if (isTemplateString) {
                // If compiled already then no need to re-compile
                if ($.isFunction(_compiled[template])) {
                    // Return to continue chaining
                    return setTemplate(this, $this, template, dataOrOptions, options);
                }

                // Not compiled

                // Get the first selection only
                $selector = $(template).first();

                // If the selector doesn't exist for whatever reason, then set to null
                if ($selector.length === 0) {
                    $selector = null;
                }
                // If a valid jQuery selector object
            } else if (template instanceof $ && $.isNumeric(template.length) && template.length > 0) {
                // Get the first selection only
                $selector = template.first();

                // Get the selector name for using with the compiled object literal
                // If the selector property doesn't exist, then this will be set to null
                template = $selector.selector !== undefined ? $selector.selector : null;

                // If compiled already then no need to re-compile
                if ($.isFunction(_compiled[template])) {
                    // Return to continue chaining
                    return setTemplate(this, $this, template, dataOrOptions, options);
                }
            }

            // If an invalid selector, then return this to continue chaining
            if ($selector === null || template === null || $selector.length === 0) {
                return this;
            }

            // Get the HTML of the template only
            var html = $selector.html();

            // Store the compiled template in the compiled store
            _compiled[template] = Handlebars.compile(html);

            // Return to continue chaining
            return setTemplate(this, $this, template, dataOrOptions, options);
        }

    });

    // Constants

    // The data-* attribute to distinguish a jQuery-handlebars template
    var DATA_ATTR = 'data-jquery-handlebars';

    // Removal constants. Who enjoys magic values?
    var Remove = {
        ALL: 'ALL',
        NONE: 'NONE',
        SAME: 'SAME'
    };

    // Type constants. Who enjoys magic values?
    var Type = {
        APPEND: 'APPEND',
        COMPILED: 'COMPILED',
        HTML: 'HTML',
        RAW: 'RAW'
    };

    // Fields (Private)

    // Store the compiled template(s) using the template string as the identifier i.e. key
    var _compiled = {};

    // Methods (Private)

    // Check if value is a boolean datatype
    function isBoolean(value) {
        return $.type(value) === 'boolean';
    }

    // Check if a value is a string datatype with a length greater than zero when whitespace is stripped
    function isString(value) {
        return $.type(value) === 'string' && value.trim().length > 0;
    }

    // Get the template in the content selector
    // include refers to whether to include the template in the selection
    function getTemplate($this, template, include) {
        // Get the divs with the template data attribute and optional specified template string e.g. #some-template
        var templateFind = (include && template ? '="' + template + '"' : '');
        return $this.find('div[' + DATA_ATTR + templateFind + ']');
    }

    // Remove the specified template from the content selector. If a template is not provided
    // then all template(s) that are contained within the content selector will be removed
    function removeTemplate(_this, $this, template, options) {
        // If the option has been passed to remove 'none', then respect this choice or if the template is null but the option is set to 'same'
        if (options.remove_type === Remove.NONE || (template === null && options.remove_type === Remove.SAME)) {
            // Return this to maintain chaining if there is nothing to remove
            return _this;
        }

        // The include parameter will be set to true, when specifically looking for the provided template string
        var filtered = getTemplate($this, template, options.remove_type === Remove.SAME);
        if (filtered.length === 0) {
            // Return this to maintain chaining if there is nothing to remove
            return _this;
        }

        // Remove filtered nodes from the DOM
        filtered.remove();

        // Remove the template from the compiled store, if the option is true
        if (isBoolean(options.delete_compiled) && options.delete_compiled) {
            // Iterate through the filtered collection and remove the template string from the compiled store
            $.each(filtered, function (index, element) {
                // Get the data attribute for the template string if it's not null or has already been removed
                // var attribute = $(element).attr(DATA_ATTR);
                var attribute = element.getAttribute(DATA_ATTR); // Returns null or '' on error
                if (!attribute && _compiled[attribute] !== undefined) {
                    // Set to undefined to mimic deletion of the template. Using delete is not really required
                    _compiled[attribute] = undefined;
                }
            });
        }
    }

    // Set the specified template to the content selector
    function setTemplate(_this, $this, template, data, options) {
        // Override deleting from the compiled stored before passing to removeTemplate
        options.delete_compiled = false;

        // Remove template(s)
        removeTemplate(_this, $this, template, options);

        // If set to not refill and template node(s) exist, then return this
        if (isBoolean(options.refill) && !options.refill) {
            // Get the template(s) after potential removal. The parameter include has been set to true, as we are checking
            // if only the same template(s) exists (perhaps an option could/should be created)
            var filtered = getTemplate($this, template, true);
            if (filtered.length > 0) {
                return _this;
            }
        }

        // Store the parsed template with data
        var parsedTemplate = _compiled[template](data);

        // Remove the compiled template from the store if specified
        if (isBoolean(options.store_compiled) && !options.store_compiled) {
            // Set to undefined to mimic deletion of the template. Using delete is not really required
            _compiled[template] = undefined;
        }

        // Append to the content element by checking the type. Default is 'append'
        switch (options.type) {
            case Type.COMPILED:
            case Type.RAW:
                // Return the compiled template
                return parsedTemplate;

            case Type.HTML:
                // Return as HTML data
                return $(parsedTemplate);

            default:
                // Create a div element with the template appended to it
                // This contains a data-* attribute called data-jquery-handlebars for easy association
                // that it's a Handlebarjs template
                var $div = $('<div/>')
                    .attr(DATA_ATTR, template)
                    .append(parsedTemplate);

                // Append the div to content element
                $this.append($div);
                break;

        }

        return _this;
    }

    // Defaults

    $.fn.handlebars.options = {
        // Delete the template from the compiled store when a removal action is specified. Accepts true (default) or false
        delete_compiled: true,

        // Store a compiled template. This improves efficiency if using the same template continuously. Accepts true (default) or false
        store_compiled: true,

        // Allow the addition of multiple template(s) inside a content element. Accepts true (default) or false
        refill: true,

        // Remove pre-existing compiled templates from the specified content element when adding/appending a template
        //
        // The following options are:
        //      'none' (default): Don't remove any template(s)
        //      'all': Remove all valid template(s) from the content element
        //      'same': Remove only those template(s) that match the provided template string
        remove_type: Remove.NONE,

        // How to output the compiled template to the specified content element
        //
        // The following options are:
        //      'append' (default): Append to the content element
        //      'html': Return a compiled template as HTML
        //      'compiled'/'raw': Return a compiled template
        type: Type.APPEND,

        // Check whether the data passed to the plugin is empty
        validate: true
    };
})(jQuery);
<|endoftext|>


<commit_before>'use strict';

module.exports = telegraph;

function telegraph (slack, args, message) {
  var userID = args.shift();
  var msg = args.join(' ');
  console.log(userID);
  userID = userID.slice(2, userID.length-1);
  console.log(userID);

  var channel = slack.getChannelGroupOrDMByID(message.channel);
  channel.send('Sending a DM to: ' + userID);

  console.log('dmChannel');
  slack.openDM(userID, function (openDmData) {
    if(openDmData.ok) {
      var dmChannel = slack.getChannelGroupOrDMByID(openDmData.channel.id);
      dmChannel.send('You recieved a telegraph from <@' + message.user  + '>: ' + msg);
    }
  });
}
<commit_msg>Clean up telegraph<commit_after>'use strict';

module.exports = telegraph;

function telegraph (slack, args, message) {
  var userID = args.shift();
  var msg = args.join(' ');
  userID = userID.slice(2, userID.length-1);

  var channel = slack.getChannelGroupOrDMByID(message.channel);
  channel.send('Sending a DM to: ' + userID);

  slack.openDM(userID, function (openDmData) {
    if(openDmData.ok) {
      var dmChannel = slack.getChannelGroupOrDMByID(openDmData.channel.id);
      dmChannel.send('You recieved a telegraph from <@' + message.user  + '>: ' + msg);
    }
  });
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012 Expression organization is undefined on line 4, column 61 in Templates/Licenses/license-gpl30.txt.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * ScreenPopup
 * A generic component to which can render itself
 * @author Meine Toonen
 */
Ext.define ("viewer.components.ScreenPopup",{
    popupWin:null,
    config:{
        title: "",
        showOnStartup: null,
        details:{
            x: 100,
            y: 100,
            width : 400,
            height: 600,
            changeablePosition:true,
            changeableSize:true,
            items:null,
            position : 'center'
        }
    },
    component: null,
    constructor: function (conf){
        var me = this;
        this.initConfig(conf);
        var config = {
            title: this.title || 'Titel',
            closable: true,
            closeAction: 'hide',
            hideMode: 'offsets',
            width: parseInt(this.details.width),
            height: parseInt(this.details.height),
            resizable: Ext.JSON.decode(this.details.changeableSize),
            draggable: Ext.JSON.decode(this.details.changeablePosition),
            layout: 'fit',
            modal: false,
            renderTo: Ext.getBody(),
            autoScroll: true
        };
        if(this.details.position == 'fixed'){
            config.x=parseInt(this.details.x);
            config.y=parseInt(this.details.y);
        }

        if(this.details.items){
            config.items = this.details.items;
            config.bodyStyle= { background: '#fff'};
        }else{
            var con = document.createElement('div');
            con.style.height=  "100%";
            con.style["background"] = "#FFFFFF";
            con.style.width=  "100%";
            config.contentEl = con;
        }

        this.popupWin = Ext.create('Ext.window.Window', config);
        if(this.showOnStartup){
            this.popupWin.show();
        }
        if(config.draggable){
            this.popupWin.addListener("dragstart",this.disableBody,this);
            this.popupWin.addListener("dragend",this.enableBody,this);
        }
        if(config.resizable){
            this.popupWin.resizer.addListener("beforeresize",this.disableBody,this);
            this.popupWin.resizer.addListener("resize",this.enableBody,this);
        }
        this.popupWin.addListener('hide', function() {
            if(me.component) {
                me.component.setButtonState('normal', true);
            }
            me.enableBody();
        });
        this.popupWin.addListener('show', function() {
            if(me.component) {
                me.component.setButtonState('click', true);
            }
        });
        return this;
    },
    setComponent: function(component) {
        this.component = component;
    },
    getContentId : function (){
        return this.popupWin.contentEl.id;
    },
    show : function(){
        this.popupWin.show();
    },
    hide : function(){
        this.popupWin.hide();
    },
    disableBody : function (){
        Ext.getBody().mask();
    },
    enableBody : function(){
        Ext.getBody().unmask()
    },
    setIconClass: function(iconCls) {
        this.popupWin.setIconCls(iconCls);
    },
    isVisible: function() {
        return this.popupWin.isVisible();
    }
});<commit_msg>Fixed position of popup components is now relative to wrapper (ticket #266)<commit_after>/*
 * Copyright (C) 2012 Expression organization is undefined on line 4, column 61 in Templates/Licenses/license-gpl30.txt.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * ScreenPopup
 * A generic component to which can render itself
 * @author Meine Toonen
 */
Ext.define ("viewer.components.ScreenPopup",{
    popupWin:null,
    config:{
        title: "",
        showOnStartup: null,
        details:{
            x: 100,
            y: 100,
            width : 400,
            height: 600,
            changeablePosition:true,
            changeableSize:true,
            items:null,
            position : 'center'
        }
    },
    component: null,
    constructor: function (conf){
        var me = this;
        this.initConfig(conf);
        var config = {
            title: this.title || 'Titel',
            closable: true,
            closeAction: 'hide',
            hideMode: 'offsets',
            width: parseInt(this.details.width),
            height: parseInt(this.details.height),
            resizable: Ext.JSON.decode(this.details.changeableSize),
            draggable: Ext.JSON.decode(this.details.changeablePosition),
            layout: 'fit',
            modal: false,
            renderTo: Ext.getBody(),
            autoScroll: true
        };
        if(this.details.position == 'fixed') {
            var wrapper = Ext.get('wrapper');
            config.x = parseInt(this.details.x) + wrapper.getX();
            config.y = parseInt(this.details.y) + wrapper.getY();
        }

        if(this.details.items){
            config.items = this.details.items;
            config.bodyStyle= { background: '#fff'};
        }else{
            var con = document.createElement('div');
            con.style.height=  "100%";
            con.style["background"] = "#FFFFFF";
            con.style.width=  "100%";
            config.contentEl = con;
        }

        this.popupWin = Ext.create('Ext.window.Window', config);
        if(this.showOnStartup){
            this.popupWin.show();
        }
        if(config.draggable){
            this.popupWin.addListener("dragstart",this.disableBody,this);
            this.popupWin.addListener("dragend",this.enableBody,this);
        }
        if(config.resizable){
            this.popupWin.resizer.addListener("beforeresize",this.disableBody,this);
            this.popupWin.resizer.addListener("resize",this.enableBody,this);
        }
        this.popupWin.addListener('hide', function() {
            if(me.component) {
                me.component.setButtonState('normal', true);
            }
            me.enableBody();
        });
        this.popupWin.addListener('show', function() {
            if(me.component) {
                me.component.setButtonState('click', true);
            }
        });
        return this;
    },
    setComponent: function(component) {
        this.component = component;
    },
    getContentId : function (){
        return this.popupWin.contentEl.id;
    },
    show : function(){
        this.popupWin.show();
    },
    hide : function(){
        this.popupWin.hide();
    },
    disableBody : function (){
        Ext.getBody().mask();
    },
    enableBody : function(){
        Ext.getBody().unmask()
    },
    setIconClass: function(iconCls) {
        this.popupWin.setIconCls(iconCls);
    },
    isVisible: function() {
        return this.popupWin.isVisible();
    }
});<|endoftext|>


<commit_before>/**
 *  getScripts (jQuery shorthand extended)
 *  A lightweight asynchronous JavaScript loader
 *
 *  Copyright 2014, Marc S. Brooks (http://mbrooks.info)
 *  Licensed under the MIT license:
 *  http://www.opensource.org/licenses/mit-license.php
 *
 *  Dependencies:
 *    jquery.js
 */

(function($) {
	var methods = {
		"init" : function(files, callback) {
			return this.each(function() {
				var count = 1;

				if (!files instanceof Array || files.length == 0) {
					throw(new Error('Invalid array'));
				}

				if (callback && typeof callback !== 'function') {
					throw(new Error('Invalid callback function'));
				}

				var _callback = function() {
					if (files.length == count++ && callback) {
						callback();
					}
				};

				for (var i = 0; i < files.length; i++) {
					$.getScript(files[i])
						.done(_callback)
						.fail(function() {
							throw(new Error("Import path '" + files[i] + "' is not valid"));
						});
				}
			});
		},
	};

	$.fn.getScripts = function(method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else
		if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		}
		else {
			$.error('Method ' +  method + ' does not exist in jQuery.getScripts');
		}
	};
})(jQuery);
<commit_msg>Fixed identifier error in IE8<commit_after>/**
 *  getScripts (jQuery shorthand extended)
 *  A lightweight asynchronous JavaScript loader
 *
 *  Copyright 2014, Marc S. Brooks (http://mbrooks.info)
 *  Licensed under the MIT license:
 *  http://www.opensource.org/licenses/mit-license.php
 *
 *  Dependencies:
 *    jquery.js
 */

(function($) {
	var methods = {
		"init" : function(files, callback) {
			return this.each(function() {
				var count = 1;

				if (!files instanceof Array || files.length == 0) {
					throw(new Error('Invalid array'));
				}

				if (callback && typeof callback !== 'function') {
					throw(new Error('Invalid callback function'));
				}

				var _callback = function() {
					if (files.length == count++ && callback) {
						callback();
					}
				};

				for (var i = 0; i < files.length; i++) {
					$.getScript(files[i])
						.done(_callback)
						.fail(function() {
							throw(new Error("Import path '" + files[i] + "' is not valid"));
						});
				}
			});
		}
	};

	$.fn.getScripts = function(method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else
		if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		}
		else {
			$.error('Method ' +  method + ' does not exist in jQuery.getScripts');
		}
	};
})(jQuery);
<|endoftext|>


<commit_before>;(function($,window,undefined) {
  var pluginName = 'shapeshift',
      document = window.document,
      defaults = {
        // Features
        centerGrid: true,
        enableAnimation: true,
        enableAutoHeight: true,
        enableDrag: true,
        enableDragAnimation: true,
        enableRearrange: true,
        enableResize: true,

        // Options
        animateSpeed: 150,
        columns: null,
        dragClone: false,
        dragRate: 100,
        dragWhitelist: "*",
        dropCutoff: 0,
        dropWhitelist: "*",
        gutterX: 10,
        gutterY: 10,
        minHeight: 100,
        paddingY: 0,
        paddingX: 0,
        selector: ""
      };

  function Plugin(element, options) {
    var ss = this;
    ss.container = $(element);
    ss.options = $.extend({}, defaults, options);
    ss.init();
  }

  Plugin.prototype.init = function() {
    var ss = this;
    ss.eventSetup();
    ss.container.trigger("ss-event-arrange");
  };

  Plugin.prototype.eventSetup = function() {
    var ss = this,
        options = ss.options;

    ss.container.attr("data-ss-rearrangeable", options.enableRearrange)
    ss.container.off("ss-event-arrange").on("ss-event-arrange", function() { ss.arrange(); });
    ss.container.off("ss-event-dragreset").on("ss-event-dragreset", function() { ss.dragClear(); ss.drag(); });

    ss.dragClear();
    if(options.enableDrag) { ss.drag(); }
    if(options.enableResize) { ss.resize(); }
  }

  Plugin.prototype.arrange = function() {
    var ss = this,
        options = ss.options,
        $objects = ss.container.children(options.selector).filter(':visible'),
        positions = ss.getPositions(ss.container, false),
        animated = true;

    if($objects.filter(".ss-moving")[0]) {
      animated = options.enableDragAnimation;
    } else {
      animated = options.enableAnimation;
    }

    // Animate / Move each object into place
    for(var obj_i=0;obj_i<positions.length;obj_i++) {
      var $obj = $($objects[obj_i]);

      // Never animate the currently dragged item
      if(!$obj.hasClass("ss-moving")) {
        if(animated) {
          $obj.stop(true, false).animate(positions[obj_i], options.animateSpeed);
        } else {
          $obj.css(positions[obj_i]);
        }
      }
    }

    // Set the container height to match the tallest column
    ss.container.css("height", options.containerHeight);
  }

  Plugin.prototype.drag = function () {
    var ss = this,
        options = ss.options;

    $curContainer = ss.container;

    var $objects = ss.container.children(options.selector),
        $selected = null,
        position = null,
        dragging = false;

    // Dragging
    $objects.filter(options.dragWhitelist).draggable({
      addClasses: false,
      containment: 'document',
      zIndex: 9999,
      drag: function(e, ui) { drag(e, ui); },
      start: function(e, ui) { start(e, ui); },
      stop: function(e, ui) { stop(e); }
    });

    function start(e, ui) {
      $selected = $(e.target);
      $curContainer = $selected.parent();
      if(options.dragClone) {
        $clone = $selected.clone().insertBefore($selected).addClass("ss-clone");
      }
      $selected.addClass("ss-moving");
    }

    function stop(e) {
      $clone = $(".ss-clone");
      if($clone[0]) {
        $cloneContainer = $clone.parent();
        if($cloneContainer[0] === $selected.parent()[0]) {
          $clone.remove();
        } else {
          $clone.removeClass("ss-clone");
          $cloneContainer.trigger("ss-event-dragreset");
          $selected.parent().trigger("ss-event-dragreset");
        }
      }
      $(e.target).removeClass("ss-moving").parent().trigger("ss-event-arrange");
    }

    function drag(e, ui) {
      if(!dragging && $curContainer.data("ss-rearrangeable")) {
        dragging = true;
        position = ss.getIntendedPosition(e);
        $objects = $curContainer.children(":not(.ss-moving):visible");
        if(position != $objects.size()) {
          $target = $objects.get(position);
          $selected.insertBefore($target);
        } else {
          $target = $objects.get(position - 1);
          $selected.insertAfter($target);
        }

        if(!$target) { $selected.appendTo($curContainer); }

        $curContainer.trigger("ss-event-arrange");
        $(".ss-prev-container").trigger("ss-event-arrange");

        window.setTimeout(function() {
          dragging = false;
        }, options.dragRate)
      }
      // Manually override the elements position
      ui.position.left = e.pageX - $(e.target).parent().offset().left - (options.childWidth / 2);
      ui.position.top = e.pageY - $(e.target).parent().offset().top - ($selected.outerHeight() / 2);
    }

    // Dropping
    ss.container.droppable({
      accept: options.dropWhitelist,
      drop: function(e) { drop(e); },
      over: function(e) { over(e); }
    });

    function drop(e) {
      $selected = $(".ss-moving").removeClass("ss-moving");
      $selectedContainer = $selected.parent();
      $selectedContainer.trigger("ss-event-arrange").trigger("ss-event-dropped", $selected);
    }

    function over(e) {
      $curContainer.addClass("ss-prev-container");
      $curContainer = $(e.target).removeClass("ss-prev-container");
    }
  }

  Plugin.prototype.dragClear = function() {
    this.container.droppable().droppable('destroy');
    this.container.children().draggable().draggable('destroy');
  }

  Plugin.prototype.getPositions = function($container, ignoreSelected) {
    var ss = this,
        options = ss.options,
        $objects = $container.children(options.selector).filter(":visible"),
        columns = options.columns,
        colHeights = [],
        colWidth = null,
        gridOffset = options.paddingX,
        positions = [];

    // If we want to get the positions for all items excluding the
    // one currently being dragged.
    if(ignoreSelected) { $objects = $objects.not(".ss-moving"); }

    // Determine the width of each element.
    options.childWidth = $objects.first().outerWidth(true);

    // Determine the column width.
    colWidth = options.childWidth + options.gutterX;

    // Determine how many columns are currently active
    if(!columns) {
      columns = Math.floor($container.innerWidth() / colWidth);
      if(columns > $objects.length) { columns = $objects.length; }
    }

    // Offset the grid to center it.
    if(options.centerGrid) {
      gridOffset = Math.floor((($container.innerWidth() / colWidth) % 1 * colWidth) / 2) + (options.gutterX / 2);
      gridOffset = Math.floor(($container.innerWidth() - (columns * colWidth)) / 2)
    }

    // Create an array element for each column, which is then
    // used to store that columns current height.
    for(var i=0;i<columns;i++) {colHeights.push(options.paddingY);}

    // Loop over each element and determine what column it fits into
    for(var obj_i=0;obj_i<$objects.length;obj_i++) {
      var $obj = $($objects[obj_i]),
          col = $.inArray(Math.min.apply(window,colHeights), colHeights),
          height = $obj.outerHeight(true) + options.gutterY,
          offsetX = (colWidth * col) + gridOffset,
          offsetY = colHeights[col];

      // Store the position to animate into place later
      attributes = { left: offsetX, top: offsetY };
      positions[obj_i] = attributes;

      // Increase the calculated total height of the current column
      colHeights[col] += height;
    }
    // Store the height of the tallest column
    if(options.enableAutoHeight){
      height = Math.max.apply(Math,colHeights);
      if(height < options.minHeight) { height = options.minHeight; }
      options.containerHeight = height;
    }
    return positions;
  }

  Plugin.prototype.getIntendedPosition = function(e) {
    var ss = this,
        options = ss.options,
        $selected = $(".ss-moving"),
        $container = $selected.parent(),
        chosenIndex = 0,
        selectedX = $selected.position().left + (options.childWidth / 2),
        selectedY = $selected.position().top + ($selected.outerHeight() / 2),
        shortestDistance = 9999,
        positions = ss.getPositions($container, true),
        endCap = positions.length - options.dropCutoff;

    // Go over all of those positions and figure out
    // which is the closest to the cursor.
    for(var hov_i=0;hov_i<positions.length;hov_i++) {
      // If we are able to insert at this index position
      if(hov_i < endCap) {
        attributes = positions[hov_i];

        // If the current item is to the bottom right of the current object position
        if(selectedX > attributes.left && selectedY > attributes.top) {
          var xDist = selectedX - attributes.left,
              yDist = selectedY - attributes.top;

          distance = Math.sqrt((xDist * xDist) + (yDist * yDist));

          // If this is the shortest distance so far
          if(distance < shortestDistance) {
            shortestDistance = distance;
            chosenIndex = hov_i;

            // If this is the last item, and we are below it or to the right,
            // then we may want to insert it as the last item.
            if(hov_i === positions.length - 1) {
              var $object = $container.children().not(".ss-moving").last();
              if(yDist > ($object.outerHeight() * .9) || xDist > options.childWidth * .9) {
                chosenIndex++;
              }
            }
          }
        }
      }
    }
    // Return the intended index position
    return chosenIndex;
  }

  Plugin.prototype.resize = function () {
    var ss = this,
        resizing = false;

    $(window).on("resize", function() {
      if(!resizing) {
        resizing = true;
        ss.container.trigger("ss-event-arrange");
        setTimeout(function() {
          resizing = false;
          ss.container.trigger("ss-event-arrange");
        }, 300);
      }
    });
  }

  // Prevent against multiple instantiations
  $.fn[pluginName] = function (options) {
    return this.each(function () {
      $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
    });
  }

}(jQuery, window));<commit_msg>Better resize timing<commit_after>;(function($,window,undefined) {
  var pluginName = 'shapeshift',
      document = window.document,
      defaults = {
        // Features
        centerGrid: true,
        enableAnimation: true,
        enableAutoHeight: true,
        enableDrag: true,
        enableDragAnimation: true,
        enableRearrange: true,
        enableResize: true,

        // Options
        animateSpeed: 150,
        columns: null,
        dragClone: false,
        dragRate: 100,
        dragWhitelist: "*",
        dropCutoff: 0,
        dropWhitelist: "*",
        gutterX: 10,
        gutterY: 10,
        minHeight: 100,
        paddingY: 0,
        paddingX: 0,
        selector: ""
      };

  function Plugin(element, options) {
    var ss = this;
    ss.container = $(element);
    ss.options = $.extend({}, defaults, options);
    ss.init();
  }

  Plugin.prototype.init = function() {
    var ss = this;
    ss.eventSetup();
    ss.container.trigger("ss-event-arrange");
  };

  Plugin.prototype.eventSetup = function() {
    var ss = this,
        options = ss.options;

    ss.container.attr("data-ss-rearrangeable", options.enableRearrange)
    ss.container.off("ss-event-arrange").on("ss-event-arrange", function() { ss.arrange(); });
    ss.container.off("ss-event-dragreset").on("ss-event-dragreset", function() { ss.dragClear(); ss.drag(); });

    ss.dragClear();
    if(options.enableDrag) { ss.drag(); }
    if(options.enableResize) { ss.resize(); }
  }

  Plugin.prototype.arrange = function() {
    var ss = this,
        options = ss.options,
        $objects = ss.container.children(options.selector).filter(':visible'),
        positions = ss.getPositions(ss.container, false),
        animated = true;

    if($objects.filter(".ss-moving")[0]) {
      animated = options.enableDragAnimation;
    } else {
      animated = options.enableAnimation;
    }

    // Animate / Move each object into place
    for(var obj_i=0;obj_i<positions.length;obj_i++) {
      var $obj = $($objects[obj_i]);

      // Never animate the currently dragged item
      if(!$obj.hasClass("ss-moving")) {
        if(animated) {
          $obj.stop(true, false).animate(positions[obj_i], options.animateSpeed);
        } else {
          $obj.css(positions[obj_i]);
        }
      }
    }

    // Set the container height to match the tallest column
    ss.container.css("height", options.containerHeight);
  }

  Plugin.prototype.drag = function () {
    var ss = this,
        options = ss.options;

    $curContainer = ss.container;

    var $objects = ss.container.children(options.selector),
        $selected = null,
        position = null,
        dragging = false;

    // Dragging
    $objects.filter(options.dragWhitelist).draggable({
      addClasses: false,
      containment: 'document',
      zIndex: 9999,
      drag: function(e, ui) { drag(e, ui); },
      start: function(e, ui) { start(e, ui); },
      stop: function(e, ui) { stop(e); }
    });

    function start(e, ui) {
      $selected = $(e.target);
      $curContainer = $selected.parent();
      if(options.dragClone) {
        $clone = $selected.clone().insertBefore($selected).addClass("ss-clone");
      }
      $selected.addClass("ss-moving");
    }

    function stop(e) {
      $clone = $(".ss-clone");
      if($clone[0]) {
        $cloneContainer = $clone.parent();
        if($cloneContainer[0] === $selected.parent()[0]) {
          $clone.remove();
        } else {
          $clone.removeClass("ss-clone");
          $cloneContainer.trigger("ss-event-dragreset");
          $selected.parent().trigger("ss-event-dragreset");
        }
      }
      $(e.target).removeClass("ss-moving").parent().trigger("ss-event-arrange");
    }

    function drag(e, ui) {
      if(!dragging && $curContainer.data("ss-rearrangeable")) {
        dragging = true;
        position = ss.getIntendedPosition(e);
        $objects = $curContainer.children(":not(.ss-moving):visible");
        if(position != $objects.size()) {
          $target = $objects.get(position);
          $selected.insertBefore($target);
        } else {
          $target = $objects.get(position - 1);
          $selected.insertAfter($target);
        }

        if(!$target) { $selected.appendTo($curContainer); }

        $curContainer.trigger("ss-event-arrange");
        $(".ss-prev-container").trigger("ss-event-arrange");

        window.setTimeout(function() {
          dragging = false;
        }, options.dragRate)
      }
      // Manually override the elements position
      ui.position.left = e.pageX - $(e.target).parent().offset().left - (options.childWidth / 2);
      ui.position.top = e.pageY - $(e.target).parent().offset().top - ($selected.outerHeight() / 2);
    }

    // Dropping
    ss.container.droppable({
      accept: options.dropWhitelist,
      drop: function(e) { drop(e); },
      over: function(e) { over(e); }
    });

    function drop(e) {
      $selected = $(".ss-moving").removeClass("ss-moving");
      $selectedContainer = $selected.parent();
      $selectedContainer.trigger("ss-event-arrange").trigger("ss-event-dropped", $selected);
    }

    function over(e) {
      $curContainer.addClass("ss-prev-container");
      $curContainer = $(e.target).removeClass("ss-prev-container");
    }
  }

  Plugin.prototype.dragClear = function() {
    this.container.droppable().droppable('destroy');
    this.container.children().draggable().draggable('destroy');
  }

  Plugin.prototype.getPositions = function($container, ignoreSelected) {
    var ss = this,
        options = ss.options,
        $objects = $container.children(options.selector).filter(":visible"),
        columns = options.columns,
        colHeights = [],
        colWidth = null,
        gridOffset = options.paddingX,
        positions = [];

    // If we want to get the positions for all items excluding the
    // one currently being dragged.
    if(ignoreSelected) { $objects = $objects.not(".ss-moving"); }

    // Determine the width of each element.
    options.childWidth = $objects.first().outerWidth(true);

    // Determine the column width.
    colWidth = options.childWidth + options.gutterX;

    // Determine how many columns are currently active
    if(!columns) {
      columns = Math.floor($container.innerWidth() / colWidth);
      if(columns > $objects.length) { columns = $objects.length; }
    }

    // Offset the grid to center it.
    if(options.centerGrid) {
      gridOffset = Math.floor((($container.innerWidth() / colWidth) % 1 * colWidth) / 2) + (options.gutterX / 2);
      gridOffset = Math.floor(($container.innerWidth() - (columns * colWidth)) / 2)
    }

    // Create an array element for each column, which is then
    // used to store that columns current height.
    for(var i=0;i<columns;i++) {colHeights.push(options.paddingY);}

    // Loop over each element and determine what column it fits into
    for(var obj_i=0;obj_i<$objects.length;obj_i++) {
      var $obj = $($objects[obj_i]),
          col = $.inArray(Math.min.apply(window,colHeights), colHeights),
          height = $obj.outerHeight(true) + options.gutterY,
          offsetX = (colWidth * col) + gridOffset,
          offsetY = colHeights[col];

      // Store the position to animate into place later
      attributes = { left: offsetX, top: offsetY };
      positions[obj_i] = attributes;

      // Increase the calculated total height of the current column
      colHeights[col] += height;
    }
    // Store the height of the tallest column
    if(options.enableAutoHeight){
      height = Math.max.apply(Math,colHeights);
      if(height < options.minHeight) { height = options.minHeight; }
      options.containerHeight = height;
    }
    return positions;
  }

  Plugin.prototype.getIntendedPosition = function(e) {
    var ss = this,
        options = ss.options,
        $selected = $(".ss-moving"),
        $container = $selected.parent(),
        chosenIndex = 0,
        selectedX = $selected.position().left + (options.childWidth / 2),
        selectedY = $selected.position().top + ($selected.outerHeight() / 2),
        shortestDistance = 9999,
        positions = ss.getPositions($container, true),
        endCap = positions.length - options.dropCutoff;

    // Go over all of those positions and figure out
    // which is the closest to the cursor.
    for(var hov_i=0;hov_i<positions.length;hov_i++) {
      // If we are able to insert at this index position
      if(hov_i < endCap) {
        attributes = positions[hov_i];

        // If the current item is to the bottom right of the current object position
        if(selectedX > attributes.left && selectedY > attributes.top) {
          var xDist = selectedX - attributes.left,
              yDist = selectedY - attributes.top;

          distance = Math.sqrt((xDist * xDist) + (yDist * yDist));

          // If this is the shortest distance so far
          if(distance < shortestDistance) {
            shortestDistance = distance;
            chosenIndex = hov_i;

            // If this is the last item, and we are below it or to the right,
            // then we may want to insert it as the last item.
            if(hov_i === positions.length - 1) {
              var $object = $container.children().not(".ss-moving").last();
              if(yDist > ($object.outerHeight() * .9) || xDist > options.childWidth * .9) {
                chosenIndex++;
              }
            }
          }
        }
      }
    }
    // Return the intended index position
    return chosenIndex;
  }

  Plugin.prototype.resize = function () {
    var ss = this,
        resizing = false;

    $(window).on("resize", function() {
      if(!resizing) {
        resizing = true;
        setTimeout(function() {
          resizing = false;
          ss.container.trigger("ss-event-arrange");
        }, 75);
      }
    });
  }

  // Prevent against multiple instantiations
  $.fn[pluginName] = function (options) {
    return this.each(function () {
      $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
    });
  }

}(jQuery, window));<|endoftext|>


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

/**
 * Property whose value must be a number.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var axon = require( 'AXON/axon' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Property = require( 'AXON/Property' );
  var NumberPropertyIO = require( 'AXON/NumberPropertyIO' );

  // valid values for options.units (or it can be null)
  var VALID_UNITS = [
    'amperes',
    'milliamperes',
    'becquerels',
    'centimeters',
    'centimeters-squared',
    'coulombs',
    'degrees Celsius',
    'farads',
    'kilograms',
    'grams',
    'gray',
    'henrys',
    'henries',
    'hertz',
    'joules',
    'katals',
    'kelvins',
    'liters',
    'liters/second',
    'lumens',
    'lux',
    'meters',
    'meters/second',
    'meters/second/second',
    'moles',
    'moles/liter',
    'nanometers',
    'newtons',
    'newtons/meters',
    'newtons-second/meters',
    'ohms',
    'ohm-centimeters',
    'pascals',
    'percent',
    'radians',
    'radians/second',
    'seconds',
    'siemens',
    'sieverts',
    'steradians',
    'tesla',
    'view-coordinates/second',
    'volts',
    'watts',
    'webers'
  ];

  // valid values for options.numberType to convey whether it is continuous or discrete with step size 1
  var VALID_NUMBER_TYPES = [ 'FloatingPoint', 'Integer' ];

  /**
   * @param {number} value - initial value
   * @param {Object} [options]
   * @constructor
   */
  function NumberProperty( value, options ) {

    var self = this;

    options = _.extend( {
      numberType: 'FloatingPoint', // {string} see VALID_VALUE_TYPES
      range: null, // {Range|{min:number, max:number}|null} range of the value
      phetioType: NumberPropertyIO,
      units: null // {string|null} units for the number, see VALID_UNITS
    }, options );

    assert && assert( _.includes( VALID_NUMBER_TYPES, options.numberType ), 'invalid numberType: ' + options.numberType );
    options.range && assert && assert( isValidRange( options.range ), 'invalid range: ' + options.range );
    options.units && assert && assert( _.includes( VALID_UNITS, options.units ), 'invalid units: ' + options.units );

    // @public (read-only) - used by PhET-iO in NumberPropertyIO as metadata passed to the wrapper.
    this.numberType = options.numberType;
    this.range = options.range;
    this.units = options.units;

    assert && assert( !options.valueType, 'valueType is set by NumberProperty' );
    options.valueType = 'number';

    // @private {function|false} value validation that is specific to NumberProperty, false if assertions are disabled
    this.assertNumberPropertyValidateValue = assert && function( value ) {
      assert( !( options.numberType === 'Integer' && value % 1 !== 0 ),
        'value has incorrect numberType, value=' + value + ', numberType=' + options.numberType );
      options.range && assert( value >= options.range.min && value <= options.range.max,
        'value is out of range, value=' + value + ', range=[' + options.range.min + ',' + options.range.max + ']');
    };

    // verify that validValues meet other NumberProperty-specific validation criteria
    if ( this.assertNumberPropertyValidateValue && options.validValues ) {
      options.validValues.forEach( function( value ) {
        self.assertNumberPropertyValidateValue( value );
      } );
    }

    Property.call( this, value, options );

    // Perform value validation that is specific to NumberProperty.
    assert && self.link( function( value ) {
      self.assertNumberPropertyValidateValue( value );
    } );
  }

  axon.register( 'NumberProperty', NumberProperty );

  /**
   * Validates a range value.
   * @param {{min:number, max:number}} range
   * @returns {boolean}
   */
  function isValidRange( range ) {
    return ( typeof range === 'object' ) && range.hasOwnProperty( 'min' ) && range.hasOwnProperty( 'max' );
  }

  return inherit( Property, NumberProperty );
} );<commit_msg>Fixed whitespace, see https://github.com/phetsims/axon/issues/172<commit_after>// Copyright 2016-2018, University of Colorado Boulder

/**
 * Property whose value must be a number.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var axon = require( 'AXON/axon' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Property = require( 'AXON/Property' );
  var NumberPropertyIO = require( 'AXON/NumberPropertyIO' );

  // valid values for options.units (or it can be null)
  var VALID_UNITS = [
    'amperes',
    'milliamperes',
    'becquerels',
    'centimeters',
    'centimeters-squared',
    'coulombs',
    'degrees Celsius',
    'farads',
    'kilograms',
    'grams',
    'gray',
    'henrys',
    'henries',
    'hertz',
    'joules',
    'katals',
    'kelvins',
    'liters',
    'liters/second',
    'lumens',
    'lux',
    'meters',
    'meters/second',
    'meters/second/second',
    'moles',
    'moles/liter',
    'nanometers',
    'newtons',
    'newtons/meters',
    'newtons-second/meters',
    'ohms',
    'ohm-centimeters',
    'pascals',
    'percent',
    'radians',
    'radians/second',
    'seconds',
    'siemens',
    'sieverts',
    'steradians',
    'tesla',
    'view-coordinates/second',
    'volts',
    'watts',
    'webers'
  ];

  // valid values for options.numberType to convey whether it is continuous or discrete with step size 1
  var VALID_NUMBER_TYPES = [ 'FloatingPoint', 'Integer' ];

  /**
   * @param {number} value - initial value
   * @param {Object} [options]
   * @constructor
   */
  function NumberProperty( value, options ) {

    var self = this;

    options = _.extend( {
      numberType: 'FloatingPoint', // {string} see VALID_VALUE_TYPES
      range: null, // {Range|{min:number, max:number}|null} range of the value
      phetioType: NumberPropertyIO,
      units: null // {string|null} units for the number, see VALID_UNITS
    }, options );

    assert && assert( _.includes( VALID_NUMBER_TYPES, options.numberType ), 'invalid numberType: ' + options.numberType );
    options.range && assert && assert( isValidRange( options.range ), 'invalid range: ' + options.range );
    options.units && assert && assert( _.includes( VALID_UNITS, options.units ), 'invalid units: ' + options.units );

    // @public (read-only) - used by PhET-iO in NumberPropertyIO as metadata passed to the wrapper.
    this.numberType = options.numberType;
    this.range = options.range;
    this.units = options.units;

    assert && assert( !options.valueType, 'valueType is set by NumberProperty' );
    options.valueType = 'number';

    // @private {function|false} value validation that is specific to NumberProperty, false if assertions are disabled
    this.assertNumberPropertyValidateValue = assert && function( value ) {
      assert( !( options.numberType === 'Integer' && value % 1 !== 0 ),
        'value has incorrect numberType, value=' + value + ', numberType=' + options.numberType );
      options.range && assert( value >= options.range.min && value <= options.range.max,
        'value is out of range, value=' + value + ', range=[' + options.range.min + ',' + options.range.max + ']' );
    };

    // verify that validValues meet other NumberProperty-specific validation criteria
    if ( this.assertNumberPropertyValidateValue && options.validValues ) {
      options.validValues.forEach( function( value ) {
        self.assertNumberPropertyValidateValue( value );
      } );
    }

    Property.call( this, value, options );

    // Perform value validation that is specific to NumberProperty.
    assert && self.link( function( value ) {
      self.assertNumberPropertyValidateValue( value );
    } );
  }

  axon.register( 'NumberProperty', NumberProperty );

  /**
   * Validates a range value.
   * @param {{min:number, max:number}} range
   * @returns {boolean}
   */
  function isValidRange( range ) {
    return ( typeof range === 'object' ) && range.hasOwnProperty( 'min' ) && range.hasOwnProperty( 'max' );
  }

  return inherit( Property, NumberProperty );
} );<|endoftext|>