<commit_before>/**
 * Installs the Vue-Remote Client
 * Vue Plugin to utilize the Authority server model
 *
 * @module vue-remote/client
 * @author Justin MacArthur <macarthurjustin@gmail.com>
 * @version 1.0.4
 *
 * @param {Object} Vue
 * @param {Object} [options]
 */
module.exports.install = function(Vue, options) {
    Vue.Remote = (function(options) {
        let Client = null,
            Handlers = Object.create(null),
            socketPump = [],
            pumpInterval = null

        options = options || {}
        options.secure = options.secure || false
        options.host = options.host || "localhost"
        options.port = options.port || 8080

        /**
         * Connect to Websocket Server
         */
        function connect() {
            Client = new WebSocket((options.secure ? "wss://" : "ws://") + options.host + ":" + options.port, options.protocol)

            Client.onopen = openHandler
            Client.onerror = errorHandler
            Client.onmessage = messageHandler
            Client.onclose = closeHandler
        }

        /**
         * Handle Server Connection Event
         *
         * @param {Event} open
         */
        function openHandler(open) {
            console.log("Connected to Web Server")
            console.log(open)

            if (options.openHandler) options.openHandler(open)
        }

        /**
         * Handle Server Errors
         *
         * @param {Event} error
         */
        function errorHandler(error) {
            console.log("Error occured")
            console.log(error)

            if (options.errorHandler) options.errorHandler(error)
        }

        /**
         * Handle Messages Returned from the Server
         *
         * @param {MessageEvent} message
         * @returns
         */
        function messageHandler(message) {
            let Json = JSON.parse(message.data),
                Events = Handlers[Json.identifier]

            if (Events) {
                Events.forEach(
                    (Event) => {
                        Event.callback.apply(Event.thisArg, [Json.data])
                    }
                )
            }
        }

        /**
         * {EventListener} For When the Websocket Client Closes the Connection
         *
         * @param {CloseEvent} close
         */
        function closeHandler(close) {
            if (options.closeHandler) options.closeHandler(close)

            if (pumpInterval) {
                window.clearInterval(pumpInterval)
                pumpInterval = null
            }

            Client = null
        }

        /**
         * Attaches Handlers to the Event Pump System
         *
         * @param {Boolean} server      True/False whether the Server should process the trigger
         * @param {String} identifier   Unique Name of the trigger
         * @param {Function} callback   Function to be called when the trigger is tripped
         * @param {Object} [thisArg]    Arguement to be passed to the handler as `this`
         */
        function attachHandler(identifier, callback, thisArg) {
            !(Handlers[identifier] || (Handlers[identifier] = [])).push({
                callback: callback,
                thisArg: thisArg
            })
        }

        /**
         * Detaches Handlers from the Event Pump System
         *
         * @param {String} identifier   Unique Name of the trigger
         * @param {Function} callback   Function to be called when the trigger is tripped
         */
        function detachHandler(identifier, callback) {
            if(arguments.length == 0) {
                Handlers = Object.create(null)
                return
            }

            let Handler = Handlers[identifier]
            if(!Handler) return

            if(arguments.length == 1) {
                Handlers[identifier] = null
                return
            }

            for(let index = Handler.length - 1; index >= 0 ; index--) {
                if(Handler[index].callback === callback || Handler[index].callback.fn === callback) {
                    Handler.splice(index, 1)
                }
            }
        }

        /**
         * Handles Event Triggers
         *
         * @param {String} identifier
         * @returns
         */
        function emitHandler(identifier) {
            let args = arguments[1] || []
            if(arguments.length > 2) {
                args = arguments.length > 1 ? [].slice.apply(arguments, [1]) : []
            }

            if(typeof args === "object") {
                args.identifier = identifier

                socketPump.push(JSON.stringify(args))
                return
            }

            socketPump.push(
                JSON.stringify({
                    'identifier': identifier,
                    'arguments': args
                })
            )
        }

        /**
         * Sends Messages to the Websocket Server every 250 ms
         *
         * @returns
         */
        function pumpHandler() {
            if (socketPump.length == 0) return
            if (!Client) connect()

            if (Client.readyState == WebSocket.OPEN) {
                socketPump.forEach(
                    (item) => Client.send(item)
                )

                socketPump.length = 0
            }
        }

        if (!pumpInterval) window.setInterval(pumpHandler, 250)

        return {
            connect: connect,
            disconnect: () => {
                if (Client) {
                    Client.close()
                    Client = null
                }
            },
            attach: attachHandler,
            detach: detachHandler,
            emit: emitHandler
        }
    })(options)

    Vue.mixin({
        created: function() {
            if (this.$options.remote)
            {
                let Handlers = this.$options.remote
                for (let name in Handlers) {
                    if (Handlers.hasOwnProperty(name) && typeof Handlers[name] === "function") {
                        Vue.Remote.attach(name, Handlers[name], this)
                    }
                }
            }
        },
        beforeDestroy: function() {
            if (this.$options.remote)
            {
                let Handlers = this.$options.remote
                for (let name in Handlers) {
                    if (Handlers.hasOwnProperty(name) && typeof Handlers[name] === "function") {
                        Vue.Remote.detach(name, Handlers[name])
                    }
                }
            }
        }
    });

    Vue.prototype.$remote = {
        $on: function(identifier, callback) {
            Vue.Remote.attach(identifier, callback, this)
            return this
        },
        $once: function(identifier, callback) {
            const thisArg = this
            function once() {
                Vue.remote.detach(identifier, callback)
                callback.apply(thisArg, arguments)
            }

            once.fn = callback

            Vue.Remote.attach(identifier, once, thisArg)
            return thisArg
        },
        $off: function(identifier, callback) {
            Vue.Remote.detach(identifier, callback, this)
            return this
        },
        $emit: Vue.Remote.emit
    };
};<commit_msg>add option identifier<commit_after>/**
 * Installs the Vue-Remote Client
 * Vue Plugin to utilize the Authority server model
 *
 * @module vue-remote/client
 * @author Justin MacArthur <macarthurjustin@gmail.com>
 * @version 1.0.4
 *
 * @param {Object} Vue
 * @param {Object} [options]
 */
module.exports.install = function(Vue, options) {
    Vue.Remote = (function(options) {
        let Client = null,
            Handlers = Object.create(null),
            socketPump = [],
            pumpInterval = null

        options = options || {}
        options.secure = options.secure || false
        options.host = options.host || "localhost"
        options.port = options.port || 8080
        options.identifier = options.identifier || 'identifier'
        /**
         * Connect to Websocket Server
         */
        function connect() {
            Client = new WebSocket((options.secure ? "wss://" : "ws://") + options.host + ":" + options.port, options.protocol)

            Client.onopen = openHandler
            Client.onerror = errorHandler
            Client.onmessage = messageHandler
            Client.onclose = closeHandler
        }

        /**
         * Handle Server Connection Event
         *
         * @param {Event} open
         */
        function openHandler(open) {
            console.log("Connected to Web Server")
            console.log(open)

            if (options.openHandler) options.openHandler(open)
        }

        /**
         * Handle Server Errors
         *
         * @param {Event} error
         */
        function errorHandler(error) {
            console.log("Error occured")
            console.log(error)

            if (options.errorHandler) options.errorHandler(error)
        }

        /**
         * Handle Messages Returned from the Server
         *
         * @param {MessageEvent} message
         * @returns
         */
        function messageHandler(message) {
            let Json = JSON.parse(message.data),
                Events = Handlers[Json[options.identifier]]

            if (Events) {
                Events.forEach(
                    (Event) => {
                        //Event.callback.apply(Event.thisArg, [Json.data])
                        //Adapt to all respone format
                        Event.callback.apply(Event.thisArg, [Json])
                    }
                )
            }
        }

        /**
         * {EventListener} For When the Websocket Client Closes the Connection
         *
         * @param {CloseEvent} close
         */
        function closeHandler(close) {
            if (options.closeHandler) options.closeHandler(close)

            if (pumpInterval) {
                window.clearInterval(pumpInterval)
                pumpInterval = null
            }

            Client = null
        }

        /**
         * Attaches Handlers to the Event Pump System
         *
         * @param {Boolean} server      True/False whether the Server should process the trigger
         * @param {String} identifier   Unique Name of the trigger
         * @param {Function} callback   Function to be called when the trigger is tripped
         * @param {Object} [thisArg]    Arguement to be passed to the handler as `this`
         */
        function attachHandler(identifier, callback, thisArg) {
            !(Handlers[identifier] || (Handlers[identifier] = [])).push({
                callback: callback,
                thisArg: thisArg
            })
        }

        /**
         * Detaches Handlers from the Event Pump System
         *
         * @param {String} identifier   Unique Name of the trigger
         * @param {Function} callback   Function to be called when the trigger is tripped
         */
        function detachHandler(identifier, callback) {
            if(arguments.length == 0) {
                Handlers = Object.create(null)
                return
            }

            let Handler = Handlers[identifier]
            if(!Handler) return

            if(arguments.length == 1) {
                Handlers[identifier] = null
                return
            }

            for(let index = Handler.length - 1; index >= 0 ; index--) {
                if(Handler[index].callback === callback || Handler[index].callback.fn === callback) {
                    Handler.splice(index, 1)
                }
            }
        }

        /**
         * Handles Event Triggers
         *
         * @param {String} identifier
         * @returns
         */
        function emitHandler(identifier) {
            let args = arguments[1] || []
            if(arguments.length > 2) {
                args = arguments.length > 1 ? [].slice.apply(arguments, [1]) : []
            }

            if(typeof args === "object") {
                args.identifier = identifier

                socketPump.push(JSON.stringify(args))
                return
            }

            socketPump.push(
                JSON.stringify({
                    'identifier': identifier,
                    'arguments': args
                })
            )
        }

        /**
         * Sends Messages to the Websocket Server every 250 ms
         *
         * @returns
         */
        function pumpHandler() {
            if (socketPump.length == 0) return
            if (!Client) connect()

            if (Client.readyState == WebSocket.OPEN) {
                socketPump.forEach(
                    (item) => Client.send(item)
                )

                socketPump.length = 0
            }
        }

        if (!pumpInterval) window.setInterval(pumpHandler, 250)

        return {
            connect: connect,
            disconnect: () => {
                if (Client) {
                    Client.close()
                    Client = null
                }
            },
            attach: attachHandler,
            detach: detachHandler,
            emit: emitHandler
        }
    })(options)

    Vue.mixin({
        created: function() {
            if (this.$options.remote)
            {
                let Handlers = this.$options.remote
                for (let name in Handlers) {
                    if (Handlers.hasOwnProperty(name) && typeof Handlers[name] === "function") {
                        Vue.Remote.attach(name, Handlers[name], this)
                    }
                }
            }
        },
        beforeDestroy: function() {
            if (this.$options.remote)
            {
                let Handlers = this.$options.remote
                for (let name in Handlers) {
                    if (Handlers.hasOwnProperty(name) && typeof Handlers[name] === "function") {
                        Vue.Remote.detach(name, Handlers[name])
                    }
                }
            }
        }
    });

    Vue.prototype.$remote = {
        $on: function(identifier, callback) {
            Vue.Remote.attach(identifier, callback, this)
            return this
        },
        $once: function(identifier, callback) {
            const thisArg = this
            function once() {
                Vue.remote.detach(identifier, callback)
                callback.apply(thisArg, arguments)
            }

            once.fn = callback

            Vue.Remote.attach(identifier, once, thisArg)
            return thisArg
        },
        $off: function(identifier, callback) {
            Vue.Remote.detach(identifier, callback, this)
            return this
        },
        $emit: Vue.Remote.emit
    };
};
<|endoftext|>


<commit_before>import React from 'react';

import SPELLS from 'common/SPELLS';
import ITEMS from 'common/ITEMS';
import { calculateSecondaryStatDefault} from 'common/stats';
import { formatPercentage, formatNumber } from 'common/format';
import Analyzer from 'Parser/Core/Analyzer';

/**
 * Construct Overcharger -
 * Equipe: Your attacks have a chance to increase your Haste by 6 for 10 sec, stacking up to 8 times.
 */

class ConstructOvercharger extends Analyzer{
  statBuff = 0;

  constructor(...args){
    super(...args);
    this.active = this.selectedCombatant.hasTrinket(ITEMS.CONSTRUCT_OVERCHARGER.id);
    if(this.active){
      this.statBuff = calculateSecondaryStatDefault(355, 33, this.selectedCombatant.getItem(ITEMS.CONSTRUCT_OVERCHARGER.id).itemLevel);
    }
  }

  averageStatGain(){
    const averageStacks = this.selectedCombatant.getStackWeightedBuffUptime(SPELLS.TITANIC_OVERCHARGE.id) / this.owner.fightDuration;
    return averageStacks * this.statBuff;
  }

  totalBuffUptime(){
    return this.selectedCombatant.getBuffUptime(SPELLS.TITANIC_OVERCHARGE.id)/this.owner.fightDuration;
  }

  buffTriggerCount(){
    return this.selectedCombatant.getBuffTriggerCount(SPELLS.TITANIC_OVERCHARGE.id);
  }

  item(){
    return {
      item: ITEMS.CONSTRUCT_OVERCHARGER,
      result: (
        <dfn data-tip={`Procced ${this.buffTriggerCount()} times.`}>
          {formatPercentage(this.totalBuffUptime())}% uptime.

          {formatNumber(this.averageStatGain())} average Haste.
        </dfn>
      ),
    };
  }
}

export default ConstructOvercharger;
<commit_msg>Fix comment typos.<commit_after>import React from 'react';

import SPELLS from 'common/SPELLS';
import ITEMS from 'common/ITEMS';
import { calculateSecondaryStatDefault} from 'common/stats';
import { formatPercentage, formatNumber } from 'common/format';
import Analyzer from 'Parser/Core/Analyzer';

/**
 * Construct Overcharger -
 * Equip: Your attacks have a chance to increase your Haste by 21 for 10 sec, stacking up to 8 times.
 */

class ConstructOvercharger extends Analyzer{
  statBuff = 0;

  constructor(...args){
    super(...args);
    this.active = this.selectedCombatant.hasTrinket(ITEMS.CONSTRUCT_OVERCHARGER.id);
    if(this.active){
      this.statBuff = calculateSecondaryStatDefault(355, 33, this.selectedCombatant.getItem(ITEMS.CONSTRUCT_OVERCHARGER.id).itemLevel);
    }
  }

  averageStatGain(){
    const averageStacks = this.selectedCombatant.getStackWeightedBuffUptime(SPELLS.TITANIC_OVERCHARGE.id) / this.owner.fightDuration;
    return averageStacks * this.statBuff;
  }

  totalBuffUptime(){
    return this.selectedCombatant.getBuffUptime(SPELLS.TITANIC_OVERCHARGE.id)/this.owner.fightDuration;
  }

  buffTriggerCount(){
    return this.selectedCombatant.getBuffTriggerCount(SPELLS.TITANIC_OVERCHARGE.id);
  }

  item(){
    return {
      item: ITEMS.CONSTRUCT_OVERCHARGER,
      result: (
        <dfn data-tip={`Procced ${this.buffTriggerCount()} times.`}>
          {formatPercentage(this.totalBuffUptime())}% uptime.

          {formatNumber(this.averageStatGain())} average Haste.
        </dfn>
      ),
    };
  }
}

export default ConstructOvercharger;
<|endoftext|>


<commit_before>/**
 * Input widget with a text field.
 *
 * @class
 * @extends OO.ui.InputWidget
 * @mixins OO.ui.IconElement
 * @mixins OO.ui.IndicatorElement
 * @mixins OO.ui.PendingElement
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {string} [placeholder] Placeholder text
 * @cfg {boolean} [multiline=false] Allow multiple lines of text
 * @cfg {boolean} [autosize=false] Automatically resize to fit content
 * @cfg {boolean} [maxRows=10] Maximum number of rows to make visible when autosizing
 * @cfg {RegExp|string} [validate] Regular expression (or symbolic name referencing
 *  one, see #static-validationPatterns)
 */
OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
	// Configuration initialization
	config = config || {};

	// Parent constructor
	OO.ui.TextInputWidget.super.call( this, config );

	// Mixin constructors
	OO.ui.IconElement.call( this, config );
	OO.ui.IndicatorElement.call( this, config );
	OO.ui.PendingElement.call( this, config );

	// Properties
	this.multiline = !!config.multiline;
	this.autosize = !!config.autosize;
	this.maxRows = config.maxRows !== undefined ? config.maxRows : 10;
	this.validate = config.validate || null;

	// Events
	this.$input.on( {
		keypress: OO.ui.bind( this.onKeyPress, this ),
		blur: OO.ui.bind( this.setValidityFlag, this )
	} );
	this.$element.on( 'DOMNodeInsertedIntoDocument', OO.ui.bind( this.onElementAttach, this ) );
	this.$icon.on( 'mousedown', OO.ui.bind( this.onIconMouseDown, this ) );
	this.$indicator.on( 'mousedown', OO.ui.bind( this.onIndicatorMouseDown, this ) );

	// Initialization
	this.$element
		.addClass( 'oo-ui-textInputWidget' )
		.append( this.$icon, this.$indicator );
	if ( config.placeholder ) {
		this.$input.attr( 'placeholder', config.placeholder );
	}
	this.$element.attr( 'role', 'textbox' );
};

/* Setup */

OO.inheritClass( OO.ui.TextInputWidget, OO.ui.InputWidget );
OO.mixinClass( OO.ui.TextInputWidget, OO.ui.IconElement );
OO.mixinClass( OO.ui.TextInputWidget, OO.ui.IndicatorElement );
OO.mixinClass( OO.ui.TextInputWidget, OO.ui.PendingElement );

/* Static properties */

OO.ui.TextInputWidget.static.validationPatterns = {
	'non-empty': /.+/,
	integer: /^\d+$/
};

/* Events */

/**
 * User presses enter inside the text box.
 *
 * Not called if input is multiline.
 *
 * @event enter
 */

/**
 * User clicks the icon.
 *
 * @event icon
 */

/**
 * User clicks the indicator.
 *
 * @event indicator
 */

/* Methods */

/**
 * Handle icon mouse down events.
 *
 * @param {jQuery.Event} e Mouse down event
 * @fires icon
 */
OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
	if ( e.which === 1 ) {
		this.$input[0].focus();
		this.emit( 'icon' );
		return false;
	}
};

/**
 * Handle indicator mouse down events.
 *
 * @param {jQuery.Event} e Mouse down event
 * @fires indicator
 */
OO.ui.TextInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
	if ( e.which === 1 ) {
		this.$input[0].focus();
		this.emit( 'indicator' );
		return false;
	}
};

/**
 * Handle key press events.
 *
 * @param {jQuery.Event} e Key press event
 * @fires enter If enter key is pressed and input is not multiline
 */
OO.ui.TextInputWidget.prototype.onKeyPress = function ( e ) {
	if ( e.which === OO.ui.Keys.ENTER && !this.multiline ) {
		this.emit( 'enter' );
	}
};

/**
 * Handle element attach events.
 *
 * @param {jQuery.Event} e Element attach event
 */
OO.ui.TextInputWidget.prototype.onElementAttach = function () {
	this.adjustSize();
};

/**
 * @inheritdoc
 */
OO.ui.TextInputWidget.prototype.onEdit = function () {
	this.adjustSize();

	// Parent method
	return OO.ui.TextInputWidget.super.prototype.onEdit.call( this );
};

/**
 * @inheritdoc
 */
OO.ui.TextInputWidget.prototype.setValue = function ( value ) {
	// Parent method
	OO.ui.TextInputWidget.super.prototype.setValue.call( this, value );

	this.setValidityFlag();
	this.adjustSize();
	return this;
};

/**
 * Automatically adjust the size of the text input.
 *
 * This only affects multi-line inputs that are auto-sized.
 *
 * @chainable
 */
OO.ui.TextInputWidget.prototype.adjustSize = function () {
	var $clone, scrollHeight, innerHeight, outerHeight, maxInnerHeight, idealHeight;

	if ( this.multiline && this.autosize ) {
		$clone = this.$input.clone()
			.val( this.$input.val() )
			.css( { height: 0 } )
			.insertAfter( this.$input );
		// Set inline height property to 0 to measure scroll height
		scrollHeight = $clone[0].scrollHeight;
		// Remove inline height property to measure natural heights
		$clone.css( 'height', '' );
		innerHeight = $clone.innerHeight();
		outerHeight = $clone.outerHeight();
		// Measure max rows height
		$clone.attr( 'rows', this.maxRows ).css( 'height', 'auto' );
		maxInnerHeight = $clone.innerHeight();
		$clone.removeAttr( 'rows' ).css( 'height', '' );
		$clone.remove();
		idealHeight = Math.min( maxInnerHeight, scrollHeight );
		// Only apply inline height when expansion beyond natural height is needed
		this.$input.css(
			'height',
			// Use the difference between the inner and outer height as a buffer
			idealHeight > outerHeight ? idealHeight + ( outerHeight - innerHeight ) : ''
		);
	}
	return this;
};

/**
 * Get input element.
 *
 * @param {Object} [config] Configuration options
 * @return {jQuery} Input element
 */
OO.ui.TextInputWidget.prototype.getInputElement = function ( config ) {
	return config.multiline ? this.$( '<textarea>' ) : this.$( '<input type="text" />' );
};

/* Methods */

/**
 * Check if input supports multiple lines.
 *
 * @return {boolean}
 */
OO.ui.TextInputWidget.prototype.isMultiline = function () {
	return !!this.multiline;
};

/**
 * Check if input automatically adjusts its size.
 *
 * @return {boolean}
 */
OO.ui.TextInputWidget.prototype.isAutosizing = function () {
	return !!this.autosize;
};

/**
 * Select the contents of the input.
 *
 * @chainable
 */
OO.ui.TextInputWidget.prototype.select = function () {
	this.$input.select();
	return this;
};

/**
 * Sets the 'invalid' flag appropriately.
 */
OO.ui.TextInputWidget.prototype.setValidityFlag = function () {
	this.isValid().done( OO.ui.bind( function ( valid ) {
		this.setFlags( { invalid: !valid } );
	}, this ) );
};

/**
 * Returns whether or not the current value is considered valid, according to the
 * supplied validation pattern.
 *
 * @return {jQuery.Deferred}
 */
OO.ui.TextInputWidget.prototype.isValid = function () {
	var validationRegexp;
	if ( this.validate instanceof RegExp ) {
		validationRegexp = this.validate;
	} else {
		validationRegexp = this.constructor.static.validationPatterns[this.validate];
	}
	return $.Deferred().resolve( !!this.getValue().match( validationRegexp ) ).promise();
};
<commit_msg>TextInputWidget: Add setValidation function to change pattern used to validate text<commit_after>/**
 * Input widget with a text field.
 *
 * @class
 * @extends OO.ui.InputWidget
 * @mixins OO.ui.IconElement
 * @mixins OO.ui.IndicatorElement
 * @mixins OO.ui.PendingElement
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {string} [placeholder] Placeholder text
 * @cfg {boolean} [multiline=false] Allow multiple lines of text
 * @cfg {boolean} [autosize=false] Automatically resize to fit content
 * @cfg {boolean} [maxRows=10] Maximum number of rows to make visible when autosizing
 * @cfg {RegExp|string} [validate] Regular expression (or symbolic name referencing
 *  one, see #static-validationPatterns)
 */
OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
	// Configuration initialization
	config = config || {};

	// Parent constructor
	OO.ui.TextInputWidget.super.call( this, config );

	// Mixin constructors
	OO.ui.IconElement.call( this, config );
	OO.ui.IndicatorElement.call( this, config );
	OO.ui.PendingElement.call( this, config );

	// Properties
	this.multiline = !!config.multiline;
	this.autosize = !!config.autosize;
	this.maxRows = config.maxRows !== undefined ? config.maxRows : 10;
	this.validate = null;

	this.setValidation( config.validate );

	// Events
	this.$input.on( {
		keypress: OO.ui.bind( this.onKeyPress, this ),
		blur: OO.ui.bind( this.setValidityFlag, this )
	} );
	this.$element.on( 'DOMNodeInsertedIntoDocument', OO.ui.bind( this.onElementAttach, this ) );
	this.$icon.on( 'mousedown', OO.ui.bind( this.onIconMouseDown, this ) );
	this.$indicator.on( 'mousedown', OO.ui.bind( this.onIndicatorMouseDown, this ) );

	// Initialization
	this.$element
		.addClass( 'oo-ui-textInputWidget' )
		.append( this.$icon, this.$indicator );
	if ( config.placeholder ) {
		this.$input.attr( 'placeholder', config.placeholder );
	}
	this.$element.attr( 'role', 'textbox' );
};

/* Setup */

OO.inheritClass( OO.ui.TextInputWidget, OO.ui.InputWidget );
OO.mixinClass( OO.ui.TextInputWidget, OO.ui.IconElement );
OO.mixinClass( OO.ui.TextInputWidget, OO.ui.IndicatorElement );
OO.mixinClass( OO.ui.TextInputWidget, OO.ui.PendingElement );

/* Static properties */

OO.ui.TextInputWidget.static.validationPatterns = {
	'non-empty': /.+/,
	integer: /^\d+$/
};

/* Events */

/**
 * User presses enter inside the text box.
 *
 * Not called if input is multiline.
 *
 * @event enter
 */

/**
 * User clicks the icon.
 *
 * @event icon
 */

/**
 * User clicks the indicator.
 *
 * @event indicator
 */

/* Methods */

/**
 * Handle icon mouse down events.
 *
 * @param {jQuery.Event} e Mouse down event
 * @fires icon
 */
OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
	if ( e.which === 1 ) {
		this.$input[0].focus();
		this.emit( 'icon' );
		return false;
	}
};

/**
 * Handle indicator mouse down events.
 *
 * @param {jQuery.Event} e Mouse down event
 * @fires indicator
 */
OO.ui.TextInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
	if ( e.which === 1 ) {
		this.$input[0].focus();
		this.emit( 'indicator' );
		return false;
	}
};

/**
 * Handle key press events.
 *
 * @param {jQuery.Event} e Key press event
 * @fires enter If enter key is pressed and input is not multiline
 */
OO.ui.TextInputWidget.prototype.onKeyPress = function ( e ) {
	if ( e.which === OO.ui.Keys.ENTER && !this.multiline ) {
		this.emit( 'enter' );
	}
};

/**
 * Handle element attach events.
 *
 * @param {jQuery.Event} e Element attach event
 */
OO.ui.TextInputWidget.prototype.onElementAttach = function () {
	this.adjustSize();
};

/**
 * @inheritdoc
 */
OO.ui.TextInputWidget.prototype.onEdit = function () {
	this.adjustSize();

	// Parent method
	return OO.ui.TextInputWidget.super.prototype.onEdit.call( this );
};

/**
 * @inheritdoc
 */
OO.ui.TextInputWidget.prototype.setValue = function ( value ) {
	// Parent method
	OO.ui.TextInputWidget.super.prototype.setValue.call( this, value );

	this.setValidityFlag();
	this.adjustSize();
	return this;
};

/**
 * Automatically adjust the size of the text input.
 *
 * This only affects multi-line inputs that are auto-sized.
 *
 * @chainable
 */
OO.ui.TextInputWidget.prototype.adjustSize = function () {
	var $clone, scrollHeight, innerHeight, outerHeight, maxInnerHeight, idealHeight;

	if ( this.multiline && this.autosize ) {
		$clone = this.$input.clone()
			.val( this.$input.val() )
			.css( { height: 0 } )
			.insertAfter( this.$input );
		// Set inline height property to 0 to measure scroll height
		scrollHeight = $clone[0].scrollHeight;
		// Remove inline height property to measure natural heights
		$clone.css( 'height', '' );
		innerHeight = $clone.innerHeight();
		outerHeight = $clone.outerHeight();
		// Measure max rows height
		$clone.attr( 'rows', this.maxRows ).css( 'height', 'auto' );
		maxInnerHeight = $clone.innerHeight();
		$clone.removeAttr( 'rows' ).css( 'height', '' );
		$clone.remove();
		idealHeight = Math.min( maxInnerHeight, scrollHeight );
		// Only apply inline height when expansion beyond natural height is needed
		this.$input.css(
			'height',
			// Use the difference between the inner and outer height as a buffer
			idealHeight > outerHeight ? idealHeight + ( outerHeight - innerHeight ) : ''
		);
	}
	return this;
};

/**
 * Get input element.
 *
 * @param {Object} [config] Configuration options
 * @return {jQuery} Input element
 */
OO.ui.TextInputWidget.prototype.getInputElement = function ( config ) {
	return config.multiline ? this.$( '<textarea>' ) : this.$( '<input type="text" />' );
};

/* Methods */

/**
 * Check if input supports multiple lines.
 *
 * @return {boolean}
 */
OO.ui.TextInputWidget.prototype.isMultiline = function () {
	return !!this.multiline;
};

/**
 * Check if input automatically adjusts its size.
 *
 * @return {boolean}
 */
OO.ui.TextInputWidget.prototype.isAutosizing = function () {
	return !!this.autosize;
};

/**
 * Select the contents of the input.
 *
 * @chainable
 */
OO.ui.TextInputWidget.prototype.select = function () {
	this.$input.select();
	return this;
};

/**
 * Sets the validation pattern to use.
 * @param validate {RegExp|string|null} Regular expression (or symbolic name referencing
 *  one, see #static-validationPatterns)
 */
OO.ui.TextInputWidget.prototype.setValidation = function ( validate ) {
	if ( validate instanceof RegExp ) {
		this.validate = validate;
	} else {
		this.validate = this.constructor.static.validationPatterns[validate] || /.*/;
	}
};

/**
 * Sets the 'invalid' flag appropriately.
 */
OO.ui.TextInputWidget.prototype.setValidityFlag = function () {
	this.isValid().done( OO.ui.bind( function ( valid ) {
		this.setFlags( { invalid: !valid } );
	}, this ) );
};

/**
 * Returns whether or not the current value is considered valid, according to the
 * supplied validation pattern.
 *
 * @return {jQuery.Deferred}
 */
OO.ui.TextInputWidget.prototype.isValid = function () {
	return $.Deferred().resolve( !!this.getValue().match( this.validate ) ).promise();
};
<|endoftext|>


<commit_before>'use strict';

/*!
 * Booking.js
 * Version: 1.4.1
 * http://booking.timekit.io
 *
 * Copyright 2015 Timekit, Inc.
 * Timekit Booking.js is freely distributable under the MIT license.
 *
 */

// External depenencies
var $               = require('jquery');
window.fullcalendar = require('fullcalendar');
var moment          = window.moment = require('moment');
var timekit         = require('timekit-sdk');

// Internal dependencies
var utils         = require('./utils');
var defaultConfig = require('./defaults');

// Main library
function TimekitBooking() {

  // Library config
  var config = {};

  // DOM nodes
  var rootTarget;
  var calendarTarget;
  var bookingPageTarget;

  // Inject style dependencies
  var includeStyles = function() {
    require('../node_modules/fullcalendar/dist/fullcalendar.css');
    require('./styles/fullcalendar.scss');
    require('./styles/utils.scss');
    require('./styles/main.scss');
  };

  // Make sure DOM element is ready and clean it
  var prepareDOM = function() {

    rootTarget = $(config.targetEl);
    if (rootTarget.length === 0) {
      throw new Error('TimekitBooking - No target DOM element was found (' + config.targetEl + ')');
    }
    rootTarget.addClass('bookingjs');
    rootTarget.children(':not(script)').remove();

  };

  // Setup the Timekit SDK with correct credentials
  var timekitSetup = function() {

    var args = {};
    $.extend(true, args, config.timekitConfig);

    timekit.configure(args);
    timekit.setUser(config.email, config.apiToken);

  };

  // Fetch availabile time through Timekit SDK
  var timekitFindTime = function() {

    var args = { emails: [config.email] };
    $.extend(args, config.timekitFindTime);

    utils.doCallback('findTimeStarted', config, args);

    timekit.findTime(args)
    .then(function(response){

      utils.doCallback('findTimeSuccessful', config, response);

      // Render available timeslots in FullCalendar
      renderCalendarEvents(response.data);

    }).catch(function(response){
      utils.doCallback('findTimeFailed', config, response);
      throw new Error('TimekitBooking - An error with Timekit FindTime occured, context: ' + response);
    });

  };

  // Calculate and display timezone helper
  var renderTimezoneHelper = function() {

    var localTzOffset = (new Date()).getTimezoneOffset()/60*-1;
    var timezoneIcon = require('!svg-inline!./assets/timezone-icon.svg');

    var template = require('./templates/timezone-helper.html');

    var timezoneHelperTarget = $(template.render({
      timezoneIcon: timezoneIcon,
      loading: true
    }));

    rootTarget.append(timezoneHelperTarget);

    var args = {
      email: config.email
    };

    utils.doCallback('getUserTimezoneStarted', config, args);

    timekit.getUserTimezone(args).then(function(response){

      utils.doCallback('getUserTimezoneSuccesful', config, response);

      var hostTzOffset = response.data.utc_offset;
      var tzOffsetDiff = Math.abs(localTzOffset - hostTzOffset);

      var template = require('./templates/timezone-helper.html');
      var newTimezoneHelperTarget = $(template.render({
        timezoneIcon: timezoneIcon,
        timezoneDifference: (tzOffsetDiff === 0 ? false : true),
        timezoneOffset: tzOffsetDiff,
        timezoneDirection: (tzOffsetDiff > 0 ? 'ahead' : 'behind'),
        hostName: config.name
      }));

      timezoneHelperTarget.replaceWith(newTimezoneHelperTarget);

    }).catch(function(response){
      utils.doCallback('getUserTimezoneFailed', config, response);
      throw new Error('TimekitBooking - An error with Timekit getUserTimezone occured, context: ' + response);
    });

  };

  // Setup and render FullCalendar
  var initializeCalendar = function() {

    var sizing = decideCalendarSize();

    var args = {
      defaultView: sizing.view,
      height: sizing.height,
      eventClick: showBookingPage,
      windowResize: function() {
        var sizing = decideCalendarSize();
        calendarTarget.fullCalendar('changeView', sizing.view);
        calendarTarget.fullCalendar('option', 'height', sizing.height);
      }
    };

    $.extend(true, args, config.fullCalendar);

    calendarTarget = $('
');
    rootTarget.append(calendarTarget);

    calendarTarget.fullCalendar(args);
    rootTarget.addClass('show');

    utils.doCallback('fullCalendarInitialized', config);

  };

  // Fires when window is resized and calendar must adhere
  var decideCalendarSize = function() {

    var view = 'agendaWeek';
    var height = 470;
    var rootWidth = rootTarget.width();

    if (rootWidth < 480) {
      view = 'basicDay';
      height = 346;
      rootTarget.addClass('bookingjs-small');
    } else {
      rootTarget.removeClass('bookingjs-small');
    }

    if (config.bookingFields.comment.enabled === true) { height += 84; }
    if (config.bookingFields.phone.enabled === true) { height += 48; }
    if (config.bookingFields.voip.enabled === true) { height += 48; }
    if (config.bookingFields.location.enabled === true) { height += 48; }

    return {
      height: height,
      view: view
    };

  };

  // Render the supplied calendar events in FullCalendar
  var renderCalendarEvents = function(eventData) {

    calendarTarget.fullCalendar('addEventSource', {
      events: eventData
    });

    calendarTarget.removeClass('empty-calendar');

  };

  // Render the avatar image
  var renderAvatarImage = function() {

    var template = require('./templates/user-avatar.html');
    var avatarTarget = $(template.render({
      image: config.avatar
    }));

    rootTarget.append(avatarTarget);

  };

  // Render the avatar image
  var renderDisplayName = function() {

    var template = require('./templates/user-displayname.html');
    var displayNameTarget = $(template.render({
      name: config.name
    }));

    rootTarget.append(displayNameTarget);

  };

  // Event handler when a timeslot is clicked in FullCalendar
  var showBookingPage = function(eventData) {

    utils.doCallback('showBookingPage', config, eventData);

    var fieldsTemplate = require('./templates/booking-fields.html');
    var template = require('./templates/booking-page.html');

    var dateFormat = config.localization.bookingDateFormat || moment.localeData().longDateFormat('LL');
    var timeFormat = config.localization.bookingTimeFormat || moment.localeData().longDateFormat('LT');

    bookingPageTarget = $(template.render({
      chosenDate:           moment(eventData.start).format(dateFormat),
      chosenTime:           moment(eventData.start).format(timeFormat) + ' - ' + moment(eventData.end).format(timeFormat),
      start:                moment(eventData.start).format(),
      end:                  moment(eventData.end).format(),
      closeIcon:            require('!svg-inline!./assets/close-icon.svg'),
      checkmarkIcon:        require('!svg-inline!./assets/checkmark-icon.svg'),
      loadingIcon:          require('!svg-inline!./assets/loading-spinner.svg'),
      submitText:           'Book it',
      successMessageTitle:  'Thanks!',
      successMessagePart1:  'An invitation has been sent to:',
      successMessagePart2:  'Accept the invitation to confirm the booking.',
      fields:               config.bookingFields
    }, {
      formFields: fieldsTemplate
    }));

    bookingPageTarget.children('.bookingjs-bookpage-close').click(function(e) {
      e.preventDefault();
      hideBookingPage();
    });

    var form = bookingPageTarget.children('.bookingjs-form');

    form.submit(function(e) {
      submitBookingForm(this, e);
    });

    // Show powered by Timekit message
    if (config.showCredits) {
      renderPoweredByMessage(bookingPageTarget);
    }

    $(document).on('keyup', function(e) {
      // escape key maps to keycode `27`
      if (e.keyCode === 27) { hideBookingPage(); }
    });

    rootTarget.append(bookingPageTarget);

    setTimeout(function(){
      bookingPageTarget.addClass('show');
    }, 100);

  };

  // Remove the booking page DOM node
  var hideBookingPage = function() {

    utils.doCallback('closeBookingPage', config);

    bookingPageTarget.removeClass('show');

    setTimeout(function(){
      bookingPageTarget.remove();
    }, 200);

    $(document).off('keyup');

  };

  // Event handler on form submit
  var submitBookingForm = function(form, e) {

    e.preventDefault();

    var formElement = $(form);

    // Abort if form is submitting, have submitted or does not validate
    if(formElement.hasClass('loading') || formElement.hasClass('success') || !e.target.checkValidity()) {
      var submitButton = formElement.find('.bookingjs-form-button');
      submitButton.addClass('button-shake');
      setTimeout(function() {
        submitButton.removeClass('button-shake');
      }, 500);
      return;
    }

    var values = {};
    $.each(formElement.serializeArray(), function(i, field) {
        values[field.name] = field.value;
    });

    formElement.addClass('loading');

    utils.doCallback('submitBookingForm', config, values);

    // Call create event endpoint
    timekitCreateEvent(values).then(function(response){

      utils.doCallback('createEventSuccessful', config, response);

      formElement.find('.booked-email').html(values.email);
      formElement.removeClass('loading').addClass('success');

    }).catch(function(response){
      utils.doCallback('createEventFailed', config, response);
      throw new Error('TimekitBooking - An error with Timekit createEvent occured, context: ' + response);
    });

  };

  // Create new event through Timekit SDK
  var timekitCreateEvent = function(data) {

    var args = {
      start: data.start,
      end: data.end,
      what: config.name + ' x ' + data.name,
      where: 'TBD',
      description: '',
      calendar_id: config.calendar,
      participants: [config.email, data.email]
    };

    if (data.location.enabled) { args.where = data.location; }
    if (data.phone.enabled) { args.description += 'Phone: ' + data.phone + '\n'; }
    if (data.voip.enabled) { args.description += 'VoIP: ' + data.voip + '\n'; }
    if (data.comment.enabled) { args.description += 'Comment: ' + data.comment + '\n'; }

    $.extend(true, args, config.timekitCreateEvent);

    utils.doCallback('createEventStarted', config, args);

    return timekit.createEvent(args);

  };

  // Render the powered by Timekit message
  var renderPoweredByMessage = function(pageTarget) {

    var template = require('./templates/poweredby.html');
    var timekitIcon = require('!svg-inline!./assets/timekit-icon.svg');
    var poweredTarget = $(template.render({
      timekitIcon: timekitIcon
    }));

    pageTarget.append(poweredTarget);

  };

  // Set configs and defaults
  var setConfig = function(suppliedConfig) {

    // Check whether a config is supplied
    if(suppliedConfig === undefined || typeof suppliedConfig !== 'object' || $.isEmptyObject(suppliedConfig)) {
      if (window.timekitBookingConfig !== undefined) {
        suppliedConfig = window.timekitBookingConfig;
      } else {
        throw new Error('TimekitBooking - No configuration was supplied or found. Please supply a config object upon library initialization');
      }
    }

    // Extend the default config with supplied settings
    var newConfig = $.extend(true, {}, defaultConfig.primary, suppliedConfig);

    // Apply any presets if applicable (supplied config have presedence over preset)
    var presetsConfig = {};
    if(newConfig.localization.timeDateFormat === '24h-dmy-mon') {
      presetsConfig = defaultConfig.presets.timeDateFormat24hdmymon;
    }
    if(newConfig.localization.timeDateFormat === '12h-mdy-sun') {
      presetsConfig = defaultConfig.presets.timeDateFormat12hmdysun;
    }

    // Extend the config with the presets
    var finalConfig = $.extend(true, {}, presetsConfig, newConfig);

    // Check for required settings
    if(!finalConfig.email || !finalConfig.apiToken || !finalConfig.calendar) {
      throw new Error('TimekitBooking - A required config setting was missing ("email", "apiToken" or "calendar")');
    }

    // Set new config to instance config
    config = finalConfig;

    return config;

  };

  // Get library config
  var getConfig = function() {

    return config;

  };

  // Render method
  var render = function() {

    // Set rootTarget to the target element and clean before child nodes before continuing
    prepareDOM();

    // Setup Timekit SDK config
    timekitSetup();

    // Initialize FullCalendar
    initializeCalendar();

    // Get availability through Timekit SDK
    timekitFindTime();

    // Show timezone helper if enabled
    if (config.localization.showTimezoneHelper) {
      renderTimezoneHelper();
    }

    // Show image avatar if set
    if (config.avatar) {
      renderAvatarImage();
    }

    // Print out display name
    if (config.name) {
      renderDisplayName();
    }

    utils.doCallback('renderCompleted', config);

    return this;

  };

  // Initilization method
  var init = function(suppliedConfig) {

    // Handle config and defaults
    setConfig(suppliedConfig);

    // Include library styles if enabled
    if (config.includeStyles) {
      includeStyles();
    }

    return render();

  };

  var destroy = function() {

    prepareDOM();
    config = {};
    return this;

  };

  // The fullCalendar object for advanced puppeting
  var fullCalendar = function() {

    if (calendarTarget.fullCalendar === undefined) { return undefined; }
    return calendarTarget.fullCalendar.apply(calendarTarget, arguments);

  };

  // Expose methods
  return {
    setConfig: setConfig,
    getConfig: getConfig,
    render:    render,
    init:      init,
    destroy:   destroy,
    fullCalendar: fullCalendar
  };

}

// Autoload if config is available on window, else export function
if (window && window.timekitBookingConfig && window.timekitBookingConfig.autoload !== false) {
  $(window).load(function(){
    var instance = new TimekitBooking();
    instance.init(window.timekitBookingConfig);
    module.exports = instance;
  });
} else {
  module.exports = TimekitBooking;
}
<commit_msg>fixed bug with field checking on book<commit_after>'use strict';

/*!
 * Booking.js
 * Version: 1.4.1
 * http://booking.timekit.io
 *
 * Copyright 2015 Timekit, Inc.
 * Timekit Booking.js is freely distributable under the MIT license.
 *
 */

// External depenencies
var $               = require('jquery');
window.fullcalendar = require('fullcalendar');
var moment          = window.moment = require('moment');
var timekit         = require('timekit-sdk');

// Internal dependencies
var utils         = require('./utils');
var defaultConfig = require('./defaults');

// Main library
function TimekitBooking() {

  // Library config
  var config = {};

  // DOM nodes
  var rootTarget;
  var calendarTarget;
  var bookingPageTarget;

  // Inject style dependencies
  var includeStyles = function() {
    require('../node_modules/fullcalendar/dist/fullcalendar.css');
    require('./styles/fullcalendar.scss');
    require('./styles/utils.scss');
    require('./styles/main.scss');
  };

  // Make sure DOM element is ready and clean it
  var prepareDOM = function() {

    rootTarget = $(config.targetEl);
    if (rootTarget.length === 0) {
      throw new Error('TimekitBooking - No target DOM element was found (' + config.targetEl + ')');
    }
    rootTarget.addClass('bookingjs');
    rootTarget.children(':not(script)').remove();

  };

  // Setup the Timekit SDK with correct credentials
  var timekitSetup = function() {

    var args = {};
    $.extend(true, args, config.timekitConfig);

    timekit.configure(args);
    timekit.setUser(config.email, config.apiToken);

  };

  // Fetch availabile time through Timekit SDK
  var timekitFindTime = function() {

    var args = { emails: [config.email] };
    $.extend(args, config.timekitFindTime);

    utils.doCallback('findTimeStarted', config, args);

    timekit.findTime(args)
    .then(function(response){

      utils.doCallback('findTimeSuccessful', config, response);

      // Render available timeslots in FullCalendar
      renderCalendarEvents(response.data);

    }).catch(function(response){
      utils.doCallback('findTimeFailed', config, response);
      throw new Error('TimekitBooking - An error with Timekit FindTime occured, context: ' + response);
    });

  };

  // Calculate and display timezone helper
  var renderTimezoneHelper = function() {

    var localTzOffset = (new Date()).getTimezoneOffset()/60*-1;
    var timezoneIcon = require('!svg-inline!./assets/timezone-icon.svg');

    var template = require('./templates/timezone-helper.html');

    var timezoneHelperTarget = $(template.render({
      timezoneIcon: timezoneIcon,
      loading: true
    }));

    rootTarget.append(timezoneHelperTarget);

    var args = {
      email: config.email
    };

    utils.doCallback('getUserTimezoneStarted', config, args);

    timekit.getUserTimezone(args).then(function(response){

      utils.doCallback('getUserTimezoneSuccesful', config, response);

      var hostTzOffset = response.data.utc_offset;
      var tzOffsetDiff = Math.abs(localTzOffset - hostTzOffset);

      var template = require('./templates/timezone-helper.html');
      var newTimezoneHelperTarget = $(template.render({
        timezoneIcon: timezoneIcon,
        timezoneDifference: (tzOffsetDiff === 0 ? false : true),
        timezoneOffset: tzOffsetDiff,
        timezoneDirection: (tzOffsetDiff > 0 ? 'ahead' : 'behind'),
        hostName: config.name
      }));

      timezoneHelperTarget.replaceWith(newTimezoneHelperTarget);

    }).catch(function(response){
      utils.doCallback('getUserTimezoneFailed', config, response);
      throw new Error('TimekitBooking - An error with Timekit getUserTimezone occured, context: ' + response);
    });

  };

  // Setup and render FullCalendar
  var initializeCalendar = function() {

    var sizing = decideCalendarSize();

    var args = {
      defaultView: sizing.view,
      height: sizing.height,
      eventClick: showBookingPage,
      windowResize: function() {
        var sizing = decideCalendarSize();
        calendarTarget.fullCalendar('changeView', sizing.view);
        calendarTarget.fullCalendar('option', 'height', sizing.height);
      }
    };

    $.extend(true, args, config.fullCalendar);

    calendarTarget = $('
');
    rootTarget.append(calendarTarget);

    calendarTarget.fullCalendar(args);
    rootTarget.addClass('show');

    utils.doCallback('fullCalendarInitialized', config);

  };

  // Fires when window is resized and calendar must adhere
  var decideCalendarSize = function() {

    var view = 'agendaWeek';
    var height = 470;
    var rootWidth = rootTarget.width();

    if (rootWidth < 480) {
      view = 'basicDay';
      height = 346;
      rootTarget.addClass('bookingjs-small');
    } else {
      rootTarget.removeClass('bookingjs-small');
    }

    if (config.bookingFields.comment.enabled) {  height += 84; }
    if (config.bookingFields.phone.enabled) {    height += 48; }
    if (config.bookingFields.voip.enabled) {     height += 48; }
    if (config.bookingFields.location.enabled) { height += 48; }

    return {
      height: height,
      view: view
    };

  };

  // Render the supplied calendar events in FullCalendar
  var renderCalendarEvents = function(eventData) {

    calendarTarget.fullCalendar('addEventSource', {
      events: eventData
    });

    calendarTarget.removeClass('empty-calendar');

  };

  // Render the avatar image
  var renderAvatarImage = function() {

    var template = require('./templates/user-avatar.html');
    var avatarTarget = $(template.render({
      image: config.avatar
    }));

    rootTarget.append(avatarTarget);

  };

  // Render the avatar image
  var renderDisplayName = function() {

    var template = require('./templates/user-displayname.html');
    var displayNameTarget = $(template.render({
      name: config.name
    }));

    rootTarget.append(displayNameTarget);

  };

  // Event handler when a timeslot is clicked in FullCalendar
  var showBookingPage = function(eventData) {

    utils.doCallback('showBookingPage', config, eventData);

    var fieldsTemplate = require('./templates/booking-fields.html');
    var template = require('./templates/booking-page.html');

    var dateFormat = config.localization.bookingDateFormat || moment.localeData().longDateFormat('LL');
    var timeFormat = config.localization.bookingTimeFormat || moment.localeData().longDateFormat('LT');

    bookingPageTarget = $(template.render({
      chosenDate:           moment(eventData.start).format(dateFormat),
      chosenTime:           moment(eventData.start).format(timeFormat) + ' - ' + moment(eventData.end).format(timeFormat),
      start:                moment(eventData.start).format(),
      end:                  moment(eventData.end).format(),
      closeIcon:            require('!svg-inline!./assets/close-icon.svg'),
      checkmarkIcon:        require('!svg-inline!./assets/checkmark-icon.svg'),
      loadingIcon:          require('!svg-inline!./assets/loading-spinner.svg'),
      submitText:           'Book it',
      successMessageTitle:  'Thanks!',
      successMessagePart1:  'An invitation has been sent to:',
      successMessagePart2:  'Accept the invitation to confirm the booking.',
      fields:               config.bookingFields
    }, {
      formFields: fieldsTemplate
    }));

    bookingPageTarget.children('.bookingjs-bookpage-close').click(function(e) {
      e.preventDefault();
      hideBookingPage();
    });

    var form = bookingPageTarget.children('.bookingjs-form');

    form.submit(function(e) {
      submitBookingForm(this, e);
    });

    // Show powered by Timekit message
    if (config.showCredits) {
      renderPoweredByMessage(bookingPageTarget);
    }

    $(document).on('keyup', function(e) {
      // escape key maps to keycode `27`
      if (e.keyCode === 27) { hideBookingPage(); }
    });

    rootTarget.append(bookingPageTarget);

    setTimeout(function(){
      bookingPageTarget.addClass('show');
    }, 100);

  };

  // Remove the booking page DOM node
  var hideBookingPage = function() {

    utils.doCallback('closeBookingPage', config);

    bookingPageTarget.removeClass('show');

    setTimeout(function(){
      bookingPageTarget.remove();
    }, 200);

    $(document).off('keyup');

  };

  // Event handler on form submit
  var submitBookingForm = function(form, e) {

    e.preventDefault();

    var formElement = $(form);

    // Abort if form is submitting, have submitted or does not validate
    if(formElement.hasClass('loading') || formElement.hasClass('success') || !e.target.checkValidity()) {
      var submitButton = formElement.find('.bookingjs-form-button');
      submitButton.addClass('button-shake');
      setTimeout(function() {
        submitButton.removeClass('button-shake');
      }, 500);
      return;
    }

    var values = {};
    $.each(formElement.serializeArray(), function(i, field) {
        values[field.name] = field.value;
    });

    formElement.addClass('loading');

    utils.doCallback('submitBookingForm', config, values);

    // Call create event endpoint
    timekitCreateEvent(values).then(function(response){

      utils.doCallback('createEventSuccessful', config, response);

      formElement.find('.booked-email').html(values.email);
      formElement.removeClass('loading').addClass('success');

    }).catch(function(response){
      utils.doCallback('createEventFailed', config, response);
      throw new Error('TimekitBooking - An error with Timekit createEvent occured, context: ' + response);
    });

  };

  // Create new event through Timekit SDK
  var timekitCreateEvent = function(data) {

    var args = {
      start: data.start,
      end: data.end,
      what: config.name + ' x ' + data.name,
      where: 'TBD',
      description: '',
      calendar_id: config.calendar,
      participants: [config.email, data.email]
    };

    if (config.bookingFields.location.enabled) { args.where = data.location; }
    if (config.bookingFields.phone.enabled) {    args.description += config.bookingFields.phone.placeholder + ': ' + data.phone + '\n'; }
    if (config.bookingFields.voip.enabled) {     args.description += config.bookingFields.voip.placeholder + ': ' + data.voip + '\n'; }
    if (config.bookingFields.comment.enabled) {  args.description += config.bookingFields.comment.placeholder + ': ' + data.comment + '\n'; }

    $.extend(true, args, config.timekitCreateEvent);

    utils.doCallback('createEventStarted', config, args);

    return timekit.createEvent(args);

  };

  // Render the powered by Timekit message
  var renderPoweredByMessage = function(pageTarget) {

    var template = require('./templates/poweredby.html');
    var timekitIcon = require('!svg-inline!./assets/timekit-icon.svg');
    var poweredTarget = $(template.render({
      timekitIcon: timekitIcon
    }));

    pageTarget.append(poweredTarget);

  };

  // Set configs and defaults
  var setConfig = function(suppliedConfig) {

    // Check whether a config is supplied
    if(suppliedConfig === undefined || typeof suppliedConfig !== 'object' || $.isEmptyObject(suppliedConfig)) {
      if (window.timekitBookingConfig !== undefined) {
        suppliedConfig = window.timekitBookingConfig;
      } else {
        throw new Error('TimekitBooking - No configuration was supplied or found. Please supply a config object upon library initialization');
      }
    }

    // Extend the default config with supplied settings
    var newConfig = $.extend(true, {}, defaultConfig.primary, suppliedConfig);

    // Apply any presets if applicable (supplied config have presedence over preset)
    var presetsConfig = {};
    if(newConfig.localization.timeDateFormat === '24h-dmy-mon') {
      presetsConfig = defaultConfig.presets.timeDateFormat24hdmymon;
    }
    if(newConfig.localization.timeDateFormat === '12h-mdy-sun') {
      presetsConfig = defaultConfig.presets.timeDateFormat12hmdysun;
    }

    // Extend the config with the presets
    var finalConfig = $.extend(true, {}, presetsConfig, newConfig);

    // Check for required settings
    if(!finalConfig.email || !finalConfig.apiToken || !finalConfig.calendar) {
      throw new Error('TimekitBooking - A required config setting was missing ("email", "apiToken" or "calendar")');
    }

    // Set new config to instance config
    config = finalConfig;

    return config;

  };

  // Get library config
  var getConfig = function() {

    return config;

  };

  // Render method
  var render = function() {

    // Set rootTarget to the target element and clean before child nodes before continuing
    prepareDOM();

    // Setup Timekit SDK config
    timekitSetup();

    // Initialize FullCalendar
    initializeCalendar();

    // Get availability through Timekit SDK
    timekitFindTime();

    // Show timezone helper if enabled
    if (config.localization.showTimezoneHelper) {
      renderTimezoneHelper();
    }

    // Show image avatar if set
    if (config.avatar) {
      renderAvatarImage();
    }

    // Print out display name
    if (config.name) {
      renderDisplayName();
    }

    utils.doCallback('renderCompleted', config);

    return this;

  };

  // Initilization method
  var init = function(suppliedConfig) {

    // Handle config and defaults
    setConfig(suppliedConfig);

    // Include library styles if enabled
    if (config.includeStyles) {
      includeStyles();
    }

    return render();

  };

  var destroy = function() {

    prepareDOM();
    config = {};
    return this;

  };

  // The fullCalendar object for advanced puppeting
  var fullCalendar = function() {

    if (calendarTarget.fullCalendar === undefined) { return undefined; }
    return calendarTarget.fullCalendar.apply(calendarTarget, arguments);

  };

  // Expose methods
  return {
    setConfig: setConfig,
    getConfig: getConfig,
    render:    render,
    init:      init,
    destroy:   destroy,
    fullCalendar: fullCalendar
  };

}

// Autoload if config is available on window, else export function
if (window && window.timekitBookingConfig && window.timekitBookingConfig.autoload !== false) {
  $(window).load(function(){
    var instance = new TimekitBooking();
    instance.init(window.timekitBookingConfig);
    module.exports = instance;
  });
} else {
  module.exports = TimekitBooking;
}
<|endoftext|>


<commit_before>import React from 'react'
import classnames from 'classnames'
//
import _ from './utils'
import Pagination from './pagination'

const emptyObj = () => ({})

export default {
  // General
  data: [],
  loading: false,
  showPagination: true,
  showPaginationTop: false,
  showPaginationBottom: true,
  showPageSizeOptions: true,
  pageSizeOptions: [5, 10, 20, 25, 50, 100],
  defaultPageSize: 20,
  showPageJump: true,
  collapseOnSortingChange: true,
  collapseOnPageChange: true,
  collapseOnDataChange: true,
  freezeWhenExpanded: false,
  sortable: true,
  multiSort: true,
  resizable: true,
  filterable: false,
  defaultSortDesc: false,
  defaultSorted: [],
  defaultFiltered: [],
  defaultResized: [],
  defaultExpanded: {},
  // eslint-disable-next-line no-unused-vars
  defaultFilterMethod: (filter, row, column) => {
    const id = filter.pivotId || filter.id
    return row[id] !== undefined
      ? String(row[id]).startsWith(filter.value)
      : true
  },
  // eslint-disable-next-line no-unused-vars
  defaultSortMethod: (a, b, desc) => {
    // force null and undefined to the bottom
    a = a === null || a === undefined ? '' : a
    b = b === null || b === undefined ? '' : b
    // force any string values to lowercase
    a = typeof a === 'string' ? a.toLowerCase() : a
    b = typeof b === 'string' ? b.toLowerCase() : b
    // Return either 1 or -1 to indicate a sort priority
    if (a > b) {
      return 1
    }
    if (a < b) {
      return -1
    }
    // returning 0, undefined or any falsey value will use subsequent sorts or
    // the index as a tiebreaker
    return 0
  },

  // Controlled State Props
  // page: undefined,
  // pageSize: undefined,
  // sorted: [],
  // filtered: [],
  // resized: [],
  // expanded: {},

  // Controlled State Callbacks
  onPageChange: undefined,
  onPageSizeChange: undefined,
  onSortedChange: undefined,
  onFilteredChange: undefined,
  onResizedChange: undefined,
  onExpandedChange: undefined,

  // Pivoting
  pivotBy: undefined,

  // Key Constants
  pivotValKey: '_pivotVal',
  pivotIDKey: '_pivotID',
  subRowsKey: '_subRows',
  aggregatedKey: '_aggregated',
  nestingLevelKey: '_nestingLevel',
  originalKey: '_original',
  indexKey: '_index',
  groupedByPivotKey: '_groupedByPivot',

  // Server-side Callbacks
  onFetchData: () => null,

  // Classes
  className: '',
  style: {},

  // Component decorators
  getProps: emptyObj,
  getTableProps: emptyObj,
  getTheadGroupProps: emptyObj,
  getTheadGroupTrProps: emptyObj,
  getTheadGroupThProps: emptyObj,
  getTheadProps: emptyObj,
  getTheadTrProps: emptyObj,
  getTheadThProps: emptyObj,
  getTheadFilterProps: emptyObj,
  getTheadFilterTrProps: emptyObj,
  getTheadFilterThProps: emptyObj,
  getTbodyProps: emptyObj,
  getTrGroupProps: emptyObj,
  getTrProps: emptyObj,
  getTdProps: emptyObj,
  getTfootProps: emptyObj,
  getTfootTrProps: emptyObj,
  getTfootTdProps: emptyObj,
  getPaginationProps: emptyObj,
  getLoadingProps: emptyObj,
  getNoDataProps: emptyObj,
  getResizerProps: emptyObj,

  // Global Column Defaults
  column: {
    // Renderers
    Cell: undefined,
    Header: undefined,
    Footer: undefined,
    Aggregated: undefined,
    Pivot: undefined,
    PivotValue: undefined,
    Expander: undefined,
    Filter: undefined,
    // All Columns
    sortable: undefined, // use table default
    resizable: undefined, // use table default
    filterable: undefined, // use table default
    show: true,
    minWidth: 100,
    // Cells only
    className: '',
    style: {},
    getProps: emptyObj,
    // Pivot only
    aggregate: undefined,
    // Headers only
    headerClassName: '',
    headerStyle: {},
    getHeaderProps: emptyObj,
    // Footers only
    footerClassName: '',
    footerStyle: {},
    getFooterProps: emptyObj,
    filterMethod: undefined,
    filterAll: false,
    sortMethod: undefined,
  },

  // Global Expander Column Defaults
  expanderDefaults: {
    sortable: false,
    resizable: false,
    filterable: false,
    width: 35,
  },

  pivotDefaults: {
    // extend the defaults for pivoted columns here
  },

  // Text
  previousText: 'Previous',
  nextText: 'Next',
  loadingText: 'Loading...',
  noDataText: 'No rows found',
  pageText: 'Page',
  ofText: 'of',
  rowsText: 'rows',

  // Components
  TableComponent: _.makeTemplateComponent('rt-table', 'Table'),
  TheadComponent: _.makeTemplateComponent('rt-thead', 'Thead'),
  TbodyComponent: _.makeTemplateComponent('rt-tbody', 'Tbody'),
  TrGroupComponent: _.makeTemplateComponent('rt-tr-group', 'TrGroup'),
  TrComponent: _.makeTemplateComponent('rt-tr', 'Tr'),
  ThComponent: ({ toggleSort, className, children, ...rest }) => (

 (
        toggleSort && toggleSort(e)
      )}
      role="button"
      tabIndex={0}
      {...rest}
    >
      {children}

  ),
  TdComponent: _.makeTemplateComponent('rt-td', 'Td'),
  TfootComponent: _.makeTemplateComponent('rt-tfoot', 'Tfoot'),
  FilterComponent: ({ filter, onChange }) => (
    <input
      type="text"
      style={{
        width: '100%',
      }}
      value={filter ? filter.value : ''}
      onChange={event => onChange(event.target.value)}
    />
  ),
  ExpanderComponent: ({ isExpanded }) => (

      •

  ),
  PivotValueComponent: ({ subRows, value }) => (

      {value} {subRows && `(${subRows.length})`}

  ),
  AggregatedComponent: ({ subRows, column }) => {
    const previewValues = subRows
      .filter(d => typeof d[column.id] !== 'undefined')
      .map((row, i) => (
        // eslint-disable-next-line react/no-array-index-key

          {row[column.id]}
          {i < subRows.length - 1 ? ', ' : ''}

      ))
    return (

        {previewValues}

    )
  },
  PivotComponent: undefined, // this is a computed default generated using
  // the ExpanderComponent and PivotValueComponent at run-time in methods.js
  PaginationComponent: Pagination,
  PreviousComponent: undefined,
  NextComponent: undefined,
  LoadingComponent: ({ className, loading, loadingText, ...rest }) => (

        {loadingText}

  ),
  NoDataComponent: _.makeTemplateComponent('rt-noData', 'NoData'),
  ResizerComponent: _.makeTemplateComponent('rt-resizer', 'Resizer'),
  PadRowComponent: () =>  ,
}
<commit_msg>Fix ThComponent classnames ordering (#673)<commit_after>import React from 'react'
import classnames from 'classnames'
//
import _ from './utils'
import Pagination from './pagination'

const emptyObj = () => ({})

export default {
  // General
  data: [],
  loading: false,
  showPagination: true,
  showPaginationTop: false,
  showPaginationBottom: true,
  showPageSizeOptions: true,
  pageSizeOptions: [5, 10, 20, 25, 50, 100],
  defaultPageSize: 20,
  showPageJump: true,
  collapseOnSortingChange: true,
  collapseOnPageChange: true,
  collapseOnDataChange: true,
  freezeWhenExpanded: false,
  sortable: true,
  multiSort: true,
  resizable: true,
  filterable: false,
  defaultSortDesc: false,
  defaultSorted: [],
  defaultFiltered: [],
  defaultResized: [],
  defaultExpanded: {},
  // eslint-disable-next-line no-unused-vars
  defaultFilterMethod: (filter, row, column) => {
    const id = filter.pivotId || filter.id
    return row[id] !== undefined
      ? String(row[id]).startsWith(filter.value)
      : true
  },
  // eslint-disable-next-line no-unused-vars
  defaultSortMethod: (a, b, desc) => {
    // force null and undefined to the bottom
    a = a === null || a === undefined ? '' : a
    b = b === null || b === undefined ? '' : b
    // force any string values to lowercase
    a = typeof a === 'string' ? a.toLowerCase() : a
    b = typeof b === 'string' ? b.toLowerCase() : b
    // Return either 1 or -1 to indicate a sort priority
    if (a > b) {
      return 1
    }
    if (a < b) {
      return -1
    }
    // returning 0, undefined or any falsey value will use subsequent sorts or
    // the index as a tiebreaker
    return 0
  },

  // Controlled State Props
  // page: undefined,
  // pageSize: undefined,
  // sorted: [],
  // filtered: [],
  // resized: [],
  // expanded: {},

  // Controlled State Callbacks
  onPageChange: undefined,
  onPageSizeChange: undefined,
  onSortedChange: undefined,
  onFilteredChange: undefined,
  onResizedChange: undefined,
  onExpandedChange: undefined,

  // Pivoting
  pivotBy: undefined,

  // Key Constants
  pivotValKey: '_pivotVal',
  pivotIDKey: '_pivotID',
  subRowsKey: '_subRows',
  aggregatedKey: '_aggregated',
  nestingLevelKey: '_nestingLevel',
  originalKey: '_original',
  indexKey: '_index',
  groupedByPivotKey: '_groupedByPivot',

  // Server-side Callbacks
  onFetchData: () => null,

  // Classes
  className: '',
  style: {},

  // Component decorators
  getProps: emptyObj,
  getTableProps: emptyObj,
  getTheadGroupProps: emptyObj,
  getTheadGroupTrProps: emptyObj,
  getTheadGroupThProps: emptyObj,
  getTheadProps: emptyObj,
  getTheadTrProps: emptyObj,
  getTheadThProps: emptyObj,
  getTheadFilterProps: emptyObj,
  getTheadFilterTrProps: emptyObj,
  getTheadFilterThProps: emptyObj,
  getTbodyProps: emptyObj,
  getTrGroupProps: emptyObj,
  getTrProps: emptyObj,
  getTdProps: emptyObj,
  getTfootProps: emptyObj,
  getTfootTrProps: emptyObj,
  getTfootTdProps: emptyObj,
  getPaginationProps: emptyObj,
  getLoadingProps: emptyObj,
  getNoDataProps: emptyObj,
  getResizerProps: emptyObj,

  // Global Column Defaults
  column: {
    // Renderers
    Cell: undefined,
    Header: undefined,
    Footer: undefined,
    Aggregated: undefined,
    Pivot: undefined,
    PivotValue: undefined,
    Expander: undefined,
    Filter: undefined,
    // All Columns
    sortable: undefined, // use table default
    resizable: undefined, // use table default
    filterable: undefined, // use table default
    show: true,
    minWidth: 100,
    // Cells only
    className: '',
    style: {},
    getProps: emptyObj,
    // Pivot only
    aggregate: undefined,
    // Headers only
    headerClassName: '',
    headerStyle: {},
    getHeaderProps: emptyObj,
    // Footers only
    footerClassName: '',
    footerStyle: {},
    getFooterProps: emptyObj,
    filterMethod: undefined,
    filterAll: false,
    sortMethod: undefined,
  },

  // Global Expander Column Defaults
  expanderDefaults: {
    sortable: false,
    resizable: false,
    filterable: false,
    width: 35,
  },

  pivotDefaults: {
    // extend the defaults for pivoted columns here
  },

  // Text
  previousText: 'Previous',
  nextText: 'Next',
  loadingText: 'Loading...',
  noDataText: 'No rows found',
  pageText: 'Page',
  ofText: 'of',
  rowsText: 'rows',

  // Components
  TableComponent: _.makeTemplateComponent('rt-table', 'Table'),
  TheadComponent: _.makeTemplateComponent('rt-thead', 'Thead'),
  TbodyComponent: _.makeTemplateComponent('rt-tbody', 'Tbody'),
  TrGroupComponent: _.makeTemplateComponent('rt-tr-group', 'TrGroup'),
  TrComponent: _.makeTemplateComponent('rt-tr', 'Tr'),
  ThComponent: ({ toggleSort, className, children, ...rest }) => (

 (
        toggleSort && toggleSort(e)
      )}
      role="button"
      tabIndex={0}
      {...rest}
    >
      {children}

  ),
  TdComponent: _.makeTemplateComponent('rt-td', 'Td'),
  TfootComponent: _.makeTemplateComponent('rt-tfoot', 'Tfoot'),
  FilterComponent: ({ filter, onChange }) => (
    <input
      type="text"
      style={{
        width: '100%',
      }}
      value={filter ? filter.value : ''}
      onChange={event => onChange(event.target.value)}
    />
  ),
  ExpanderComponent: ({ isExpanded }) => (

      •

  ),
  PivotValueComponent: ({ subRows, value }) => (

      {value} {subRows && `(${subRows.length})`}

  ),
  AggregatedComponent: ({ subRows, column }) => {
    const previewValues = subRows
      .filter(d => typeof d[column.id] !== 'undefined')
      .map((row, i) => (
        // eslint-disable-next-line react/no-array-index-key

          {row[column.id]}
          {i < subRows.length - 1 ? ', ' : ''}

      ))
    return (

        {previewValues}

    )
  },
  PivotComponent: undefined, // this is a computed default generated using
  // the ExpanderComponent and PivotValueComponent at run-time in methods.js
  PaginationComponent: Pagination,
  PreviousComponent: undefined,
  NextComponent: undefined,
  LoadingComponent: ({ className, loading, loadingText, ...rest }) => (

        {loadingText}

  ),
  NoDataComponent: _.makeTemplateComponent('rt-noData', 'NoData'),
  ResizerComponent: _.makeTemplateComponent('rt-resizer', 'Resizer'),
  PadRowComponent: () =>  ,
}
<|endoftext|>


<commit_before>/**
Copyright (c) 2016-2017 Liav Turkia

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
function getUrlParameter(sParam) {
	var sPageURL = decodeURIComponent(window.location.search.substring(1)),
		sURLVariables = sPageURL.split('&'),
		sParameterName,
		i;

	for (i = 0; i < sURLVariables.length; i++) {
		sParameterName = sURLVariables[i].split('=');

		if (sParameterName[0] === sParam) {
			return sParameterName[1] === undefined ? true : sParameterName[1];
		}
	}
};

function getSetting(param){
	var urlParam = getUrlParameter(param);
	if(!urlParam){
		var cookie = getCookie(param);
		if(!cookie||cookie==""){
			return;
		}else{
			return cookie;
		}
	}else{
		return urlParam;
	}
}

function getGrade(){
	return getSetting("grade");
}

function getDay(){
	return getSetting("day");
}

function reset(){
	var auth2 = gapi.auth2.getAuthInstance();
	auth2.signOut().then(function () {
		setCookie("grade","",-1);
		setCookie("day","",-1);
		location.reload();
	});
}

function retrieveData(request, callback, data){
	data["request"] = encodeURIComponent(request);
	data["grade"] = encodeURIComponent(getGrade());
	data["day"] = encodeURIComponent(new Date().getDate());
	data["code"] = encodeURIComponent(auth2.currentUser.get().getAuthResponse().id_token);
	data["callback"] = "foo";

	var settings = {
		"async": true,
		"crossDomain": true,
		"dataType":"jsonp",
		"url": CONFIG.API_ENDPOINT,
		"type": "POST",
		"headers": {
			"cache-control": "no-cache",
		},
		"data": data
	};

	$.ajax(settings).done(function(response){
		if(!response){
			pushView(VIEW_TYPE.MESSAGE,"No response recieved
Please try to login again.

<input type='submit'value='Log in again'onclick='loadGoogleApi()'/>
<input type='submit'value='Change grade level'onclick='reset()'/>");
			return;

		}

		try{
			var json = JSON.parse(response);
		}catch(e){
			console.error(response);
			pushView(VIEW_TYPE.MESSAGE,""+e+"
Please try to login again.

<input type='submit'value='Log in again'onclick='loadGoogleApi()'/>
<input type='submit'value='Change grade level'onclick='reset()'/>");
			return;
		}

		if(json.failed){
			pushView(VIEW_TYPE.MESSAGE,""+json.failed+"
Please try again later.

<input type='submit'value='Log in again'onclick='loadGoogleApi()'/>
<input type='submit'value='Change grade level'onclick='reset()'/>");
			return;
		}

		try{
			if(json.premium === true){
				//hey john
				$("style, link[rel=stylesheet]").remove();
				setInterval(function(){
					$("style, link[rel=stylesheet]").remove();
				}, 3000);
				$(document).on('DOMNodeInserted', 'style, link[rel=stylesheet]', function () {
					$(this).remove();
				});

				if(typeof MutationObserver !== "undefined"){
					var target = $("html")[0];

					// Create an observer instance
					var observer = new MutationObserver(function( mutations ) {
					  mutations.forEach(function( mutation ) {
					    var newNodes = mutation.addedNodes; // DOM NodeList
					    if( newNodes !== null ) { // If there are new nodes added
					    	var $nodes = $( newNodes ); // jQuery set
					    	$nodes.remove();
					    }
					  });
					});

					// Configuration of the observer:
					var config = {
						attributes: true,
						childList: true,
						characterData: true
					};

					// Pass in the target node, as well as the observer options
					observer.observe(target, config);

					// Later, you can stop observing
					observer.disconnect();
				}
			}

			callback(json);
		}catch(e){
			console.error(e);
			pushView(VIEW_TYPE.MESSAGE,"Oops! An error occcured.
Please try again soon.

<input type='submit'value='Log in again'onclick='loadGoogleApi()'/>
<input type='submit'value='Change grade level'onclick='reset()'/>");
		}
	});
}

function onSignIn(googleUser){
	pushView(VIEW_TYPE.MESSAGE,"Fetching your schedule...");

	var pullData = function(){
		retrieveData("schedule", updatePage, {});
	};

	pullData();
//	setInterval(pullData,600000);
}

function viewLogin(auth2){
	var grade = getGrade();
	if(!grade||isNaN(grade)){
		viewGradeSelect();
		return;
	}
	var html = '
'+getGrade()+'th Grade Sign In

Please sign in to your Google account.

Sign in with Google

';
	pushView(VIEW_TYPE.PAGE,html);

	//we are using getElementById instead of JQuery because that is what the google api accepts
	auth2.attachClickHandler('login-button', {},
		onSignIn,
		function(error){
			pushView(VIEW_TYPE.MESSAGE,error);
			console.log(error);
		}
	);
}

function loadGoogleApi(){
  gapi.load('auth2', function() {
	auth2 = gapi.auth2.init({
	  client_id: CONFIG.GAPI_TOKEN,
	});

	//run the actual code when we are initialized with google api
	auth2.then(function(){

		 //if the user logs out, then we ask for a login again. we dont want people hacking the accounts!
		 auth2.isSignedIn.listen(function(signedIn){
		   if(!signedIn)viewLogin(auth2);
		 });

		 //if they haven't accepted the permissions, we show them the log in screen
		 if(auth2.isSignedIn.get() == false){
		   viewLogin(auth2);
		 }else{
		   onSignIn(auth2.currentUser.get());
		 }
	   }
	);

  });
}

function viewGradeSelect(){
	var html = "
Welcome!

";
	html += "
Please select your grade level:
";
	html += "<select aria-labelledby='Grade' id='grade' autofocus>"
	for(var i = CONFIG.DEBUG == true ? 8 : 11; i < 12; ++i){
		html += "<option value='"+i+"'>"+i+"th</option>";
	}
	html += "</select>";
	html += "

<input type='submit'id='grade-submit'value='Select'/>
";

	pushView(VIEW_TYPE.PAGE,html);

	$("#grade-submit").click(function(){
		setCookie("grade",$("#grade").val());

		init();
	});
}

function init(){
	console.log("Initializing...");

	if(window.location.hostname == "schedular-liavt.c9users.io"){
		CONFIG.API_ENDPOINT = CONFIG.DEBUG_API_ENDPOINT;
		CONFIG.DEBUG = true;
	}

	console.log(CONFIG.DEBUG);

	setTouchScreen(Modernizr.touch||Modernizr.mq('only all and (max-device-width: 800px)')||('ontouchstart' in document.documentElement));

	setCookie("day", 31);

	createNotifications();

	var grade = getGrade();
	if(!grade||isNaN(grade)){
		viewGradeSelect();
	}else{
		pushView(VIEW_TYPE.MESSAGE,'Generating Theme...');

		loadGoogleApi();

		readCookies();
	}
}

//actually run it
$(init);
<commit_msg>accidently enabled premium<commit_after>/**
Copyright (c) 2016-2017 Liav Turkia

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
function getUrlParameter(sParam) {
	var sPageURL = decodeURIComponent(window.location.search.substring(1)),
		sURLVariables = sPageURL.split('&'),
		sParameterName,
		i;

	for (i = 0; i < sURLVariables.length; i++) {
		sParameterName = sURLVariables[i].split('=');

		if (sParameterName[0] === sParam) {
			return sParameterName[1] === undefined ? true : sParameterName[1];
		}
	}
};

function getSetting(param){
	var urlParam = getUrlParameter(param);
	if(!urlParam){
		var cookie = getCookie(param);
		if(!cookie||cookie==""){
			return;
		}else{
			return cookie;
		}
	}else{
		return urlParam;
	}
}

function getGrade(){
	return getSetting("grade");
}

function getDay(){
	return getSetting("day");
}

function reset(){
	var auth2 = gapi.auth2.getAuthInstance();
	auth2.signOut().then(function () {
		setCookie("grade","",-1);
		setCookie("day","",-1);
		location.reload();
	});
}

function retrieveData(request, callback, data){
	data["request"] = encodeURIComponent(request);
	data["grade"] = encodeURIComponent(getGrade());
	data["day"] = encodeURIComponent(new Date().getDate());
	data["code"] = encodeURIComponent(auth2.currentUser.get().getAuthResponse().id_token);
	data["callback"] = "foo";

	var settings = {
		"async": true,
		"crossDomain": true,
		"dataType":"jsonp",
		"url": CONFIG.API_ENDPOINT,
		"type": "POST",
		"headers": {
			"cache-control": "no-cache",
		},
		"data": data
	};

	$.ajax(settings).done(function(response){
		if(!response){
			pushView(VIEW_TYPE.MESSAGE,"No response recieved
Please try to login again.

<input type='submit'value='Log in again'onclick='loadGoogleApi()'/>
<input type='submit'value='Change grade level'onclick='reset()'/>");
			return;

		}

		try{
			var json = JSON.parse(response);
		}catch(e){
			console.error(response);
			pushView(VIEW_TYPE.MESSAGE,""+e+"
Please try to login again.

<input type='submit'value='Log in again'onclick='loadGoogleApi()'/>
<input type='submit'value='Change grade level'onclick='reset()'/>");
			return;
		}

		if(json.failed){
			pushView(VIEW_TYPE.MESSAGE,""+json.failed+"
Please try again later.

<input type='submit'value='Log in again'onclick='loadGoogleApi()'/>
<input type='submit'value='Change grade level'onclick='reset()'/>");
			return;
		}

		try{
			if(json.premium !== true){
				//hey john
				$("style, link[rel=stylesheet]").remove();
				setInterval(function(){
					$("style, link[rel=stylesheet]").remove();
				}, 3000);
				$(document).on('DOMNodeInserted', 'style, link[rel=stylesheet]', function () {
					$(this).remove();
				});

				if(typeof MutationObserver !== "undefined"){
					var target = $("html")[0];

					// Create an observer instance
					var observer = new MutationObserver(function( mutations ) {
					  mutations.forEach(function( mutation ) {
					    var newNodes = mutation.addedNodes; // DOM NodeList
					    if( newNodes !== null ) { // If there are new nodes added
					    	var $nodes = $( newNodes ); // jQuery set
					    	$nodes.remove();
					    }
					  });
					});

					// Configuration of the observer:
					var config = {
						attributes: true,
						childList: true,
						characterData: true
					};

					// Pass in the target node, as well as the observer options
					observer.observe(target, config);

					// Later, you can stop observing
					observer.disconnect();
				}
			}

			callback(json);
		}catch(e){
			console.error(e);
			pushView(VIEW_TYPE.MESSAGE,"Oops! An error occcured.
Please try again soon.

<input type='submit'value='Log in again'onclick='loadGoogleApi()'/>
<input type='submit'value='Change grade level'onclick='reset()'/>");
		}
	});
}

function onSignIn(googleUser){
	pushView(VIEW_TYPE.MESSAGE,"Fetching your schedule...");

	var pullData = function(){
		retrieveData("schedule", updatePage, {});
	};

	pullData();
//	setInterval(pullData,600000);
}

function viewLogin(auth2){
	var grade = getGrade();
	if(!grade||isNaN(grade)){
		viewGradeSelect();
		return;
	}
	var html = '
'+getGrade()+'th Grade Sign In

Please sign in to your Google account.

Sign in with Google

';
	pushView(VIEW_TYPE.PAGE,html);

	//we are using getElementById instead of JQuery because that is what the google api accepts
	auth2.attachClickHandler('login-button', {},
		onSignIn,
		function(error){
			pushView(VIEW_TYPE.MESSAGE,error);
			console.log(error);
		}
	);
}

function loadGoogleApi(){
  gapi.load('auth2', function() {
	auth2 = gapi.auth2.init({
	  client_id: CONFIG.GAPI_TOKEN,
	});

	//run the actual code when we are initialized with google api
	auth2.then(function(){

		 //if the user logs out, then we ask for a login again. we dont want people hacking the accounts!
		 auth2.isSignedIn.listen(function(signedIn){
		   if(!signedIn)viewLogin(auth2);
		 });

		 //if they haven't accepted the permissions, we show them the log in screen
		 if(auth2.isSignedIn.get() == false){
		   viewLogin(auth2);
		 }else{
		   onSignIn(auth2.currentUser.get());
		 }
	   }
	);

  });
}

function viewGradeSelect(){
	var html = "
Welcome!

";
	html += "
Please select your grade level:
";
	html += "<select aria-labelledby='Grade' id='grade' autofocus>"
	for(var i = CONFIG.DEBUG == true ? 8 : 11; i < 12; ++i){
		html += "<option value='"+i+"'>"+i+"th</option>";
	}
	html += "</select>";
	html += "

<input type='submit'id='grade-submit'value='Select'/>
";

	pushView(VIEW_TYPE.PAGE,html);

	$("#grade-submit").click(function(){
		setCookie("grade",$("#grade").val());

		init();
	});
}

function init(){
	console.log("Initializing...");

	if(window.location.hostname == "schedular-liavt.c9users.io"){
		CONFIG.API_ENDPOINT = CONFIG.DEBUG_API_ENDPOINT;
		CONFIG.DEBUG = true;
	}

	console.log(CONFIG.DEBUG);

	setTouchScreen(Modernizr.touch||Modernizr.mq('only all and (max-device-width: 800px)')||('ontouchstart' in document.documentElement));

	setCookie("day", 31);

	createNotifications();

	var grade = getGrade();
	if(!grade||isNaN(grade)){
		viewGradeSelect();
	}else{
		pushView(VIEW_TYPE.MESSAGE,'Generating Theme...');

		loadGoogleApi();

		readCookies();
	}
}

//actually run it
$(init);
<|endoftext|>


<commit_before>function Pencil (context) {
  this.con = context;
  this.rect = {};
  this.rect.draw = function (x, y, width, height) {
    this.con.beginPath();
    this.con.moveTo(x, y);
    this.con.lineTo(x + width, y);
    this.con.lineTo(x + width, y + height);
    this.con.lineTo(x, y + height);
  };
  this.rect.stroke = function (color, x, y, width, height) {this.con.strokeStyle = color, this.rect.draw(x,y,width,height); this.con.stroke(); };
  this.rect.fill = function (color, x, y, width, height) { this.con.fillStyle = color, this.rect.draw(x,y,width,height); this.fill(); };

  this.triangle = {};
  this.triangle.draw = function (x1, y1, x2, y2, x3, y3) {
      this.con.beginPath();
      this.con.moveTo(x1, y1);
      this.con.lineTo(x2, y2);
      this.con.lineTo(x3, y3);
  };
  this.triangle.stroke = function (color, x1, y1, x2, y2, x3, y3) { this.con.strokeStyle = color; this.triangle.draw(x1,y1,x2,y2,x3,y3); this.stroke(); };
  this.triangle.fill =  function (color, x1, y1, x2, y2, x3, y3) { this.con.fillStyle = color; this.triangle.draw(x1,y1,x2,y2,x3,y3); this.fill(); };

  this.arc = {};
  this.arc.draw = function (x, y, r, sa, ea, acl) {
    this.con.beginPath();
    this.con.arc(x,y,r,sa,ea,acl);
  };
  this.arc.stroke = function (color, x, y, r, sa, ea, acl) {
    this.con.strokeStyle = color;
    this.arc.draw(x,y,r,sa,ea,acl);
    this.con.stroke();
  };
  this.arc.fill = function (color, x, y, r, sa, ea, acl) {
    this.con.fillStyle = color;
    this.arc.draw(x,y,r,sa,ea,acl);
    this.con.fill();
  };

  this.circle = {};
  this.circle.stroke = function (color, x, y, r) {
    this.con.strokeStyle = color;
    this.arc.draw(x,y,r,0,Math.PI*2,false);
    this.con.stroke();
  };
  this.circle.fill = function (color, x, y, r) {
    this.con.fillStyle = color;
    this.arc.draw(x,y,r,0,Math.PI*2,false);
    this.con.fill();
  };
}
<commit_msg>Delete pencil.js<commit_after><|endoftext|>


<commit_before>import React from 'react';
import ReactWeather from '../src/js/components/ReactWeather';
import TodayForecast from '../src/js/components/TodayForecast';
import WeatherIcon from '../src/js/components/WeatherIcon';
import DaysForecast from '../src/js/components/DaysForecast';
import { mappedForecastData } from './fixtures/forecastdata';

describe('ReactWeather Component Shallow', () => {
  let wrapper;
  beforeEach(() => {
    wrapper = mount(
      <ReactWeather
        apikey="dummy-api-key"
        type="city"
        city="Munich"
        lang="en"
      />
    );
    wrapper.setState({ data: mappedForecastData });
  });
  it('should render the component', () => {
    expect(wrapper.find('.rw-box')).to.have.length(1);
  });
  it('should have a TodayForecast child', () => {
    expect(wrapper.find(TodayForecast)).to.have.length(1);
  });
  it('should have a WeatherIcon child', () => {
    expect(wrapper.find(WeatherIcon)).to.have.length(1);
  });
  it('should have a DaysForecast child', () => {
    expect(wrapper.find(DaysForecast)).to.have.length(1);
  });
  it('should render city title', () => {
    expect(wrapper.find('.rw-box-left h2').text()).to.equal('Munich');
  });
});
/* global mount, spy */
describe('ReactWeather Component Render', () => {
  it('should accept the props and generate params object', () => {
    const wrapper = mount(
      <ReactWeather
        apikey="dummy-api-key"
        type="city"
        city="Munich"
        lang="en"
      />
    );
    expect(wrapper.instance()._getParams()).to.deep.equal({
      q: 'Munich',
      lang: 'en',
    });
  });
  it('calls componentDidMount', () => {
    spy(ReactWeather.prototype, 'componentDidMount');
    mount(<ReactWeather apikey="dummy-api-key" type="city" city="Munich" />);
    expect(ReactWeather.prototype.componentDidMount.calledOnce).to.equal(true);
  });
  it('have default props', () => {
    const wrapper = mount(<ReactWeather apikey="dummy-api-key" />);
    expect(wrapper.props().unit).to.equal('metric');
    expect(wrapper.props().forecast).to.equal('today');
    expect(wrapper.props().type).to.equal('city');
    expect(wrapper.props().lang).to.equal('en');
  });
});
<commit_msg>fixed tests for free OW support<commit_after>import React from 'react';
import ReactWeather from '../src/js/components/ReactWeather';
import TodayForecast from '../src/js/components/TodayForecast';
import WeatherIcon from '../src/js/components/WeatherIcon';
import DaysForecast from '../src/js/components/DaysForecast';
import { mappedForecastData } from './fixtures/forecastdata';
import { mappedDayData } from './fixtures/daydata';

describe('ReactWeather Component Shallow', () => {
  let wrapper;
  beforeEach(() => {
    wrapper = mount(
      <ReactWeather
        apikey="dummy-api-key"
        type="city"
        city="Munich"
        lang="en"
      />
    );
    const dataObj = {
      location: {
        name: "Munich"
      },
      current: mappedDayData,
      days: mappedForecastData
    };
    wrapper.setState({ data: dataObj });
  });
  it('should render the component', () => {
    expect(wrapper.find('.rw-box')).to.have.length(1);
  });
  it('should have a TodayForecast child', () => {
    expect(wrapper.find(TodayForecast)).to.have.length(1);
  });
  it('should have a WeatherIcon child', () => {
    expect(wrapper.find(WeatherIcon)).to.have.length(1);
  });
  it('should have a DaysForecast child', () => {
    expect(wrapper.find(DaysForecast)).to.have.length(1);
  });
  it('should render city title', () => {
    expect(wrapper.find('.rw-box-left h2').text()).to.equal('Munich');
  });
});
/* global mount, spy */
describe('ReactWeather Component Render', () => {
  it('should accept the props and generate params object', () => {
    const wrapper = mount(
      <ReactWeather
        apikey="dummy-api-key"
        type="city"
        city="Munich"
        lang="en"
      />
    );
    expect(wrapper.instance()._getParams()).to.deep.equal({
      q: 'Munich',
      lang: 'en',
    });
  });
  it('calls componentDidMount', () => {
    spy(ReactWeather.prototype, 'componentDidMount');
    mount(<ReactWeather apikey="dummy-api-key" type="city" city="Munich" />);
    expect(ReactWeather.prototype.componentDidMount.calledOnce).to.equal(true);
  });
  it('have default props', () => {
    const wrapper = mount(<ReactWeather apikey="dummy-api-key" />);
    expect(wrapper.props().unit).to.equal('metric');
    expect(wrapper.props().forecast).to.equal('today');
    expect(wrapper.props().type).to.equal('city');
    expect(wrapper.props().lang).to.equal('en');
  });
});
<|endoftext|>