<commit_before>/** @namespace **/
//var XMOT = XMOT || {};
goog.provide("XMOT");

/** @define {string} */
XMOT.version = '%VERSION%';

(function() {

    /**
     * A MotionFactory.
     * @interface
     */
    var MotionFactory = function() {};
    var m = MotionFactory.prototype;

    /**
     * Creates a Moveable out of the given object
     * @param {Object} object base for the Moveable
     * @param {Constraint} constraint Constrain movement
     * @return {Moveable} created Moveable
     */
    m.createMoveable = function(object, constraint){};

    /**
     * Creates an Animatable out of the given object
     * @param {Object} object base for the Animatable
     * @param {Constraint} constraint Constrain movement
     * @return {Animatable} created Animatable
     */
    m.createAnimatable = function(object, constraint){};

    /**
     * Creates a KeyframeAnimation
     * @param {string} name name
     * @param {string} type "Position" or "Orientation"
     * @param {Object} element KeyframeAnimation, keyframes and corresponding positions or orientations
     * @param {Object=} opt Options: {number} duration in ms; {number} loop; {string} interpolation; {function} callback at the end of the animation;
     * @return {KeyframeAnimation} created KeyFrameAnimation
     */
    m.createKeyframeAnimation = function(name, type, element, opt){};

    /**
     * A Moveable.
     * @interface
     */
    var Moveable = function() {};
    var p = Moveable.prototype;

    /**
     * Sets the absolute position of the Moveable in local space.
     * @param {Array.<number>} position position as 3d vector in local space
     * @return {Moveable} the Moveable
     */
    p.setPosition = function(position){};

    /**
     * Sets the absolute orientation of the Movebale in local space.
     * @param {Array.<number>} orientation orientation as quaternion in local space
     * @return {Moveable} the Moveable
     */
    p.setOrientation = function(orientation){};

    /**
     * Translate the Moveable by a given Vector.
     * @param {Array.<number>} translation 3d Vector
     * @return {Moveable} the Moveable
     */
    p.translate = function(translation){};

    /**
     * Rotates the Moveable by a given Quaternion.
     * @param {Array.<number>} rotation Quaternion
     * @return {Moveable} the Moveable
     */
    p.rotate = function(rotation){};

    /**
     * Interpolated translation over time to position in local space.
     * The animation is put into a fifo-queue and will be eventually executed.
     * @param {Array.<number>|undefined} position local space Vector
     * @param {Array.<number>|undefined} orientation orientation Quaternion
     * @param {number} time when to reach the position, in milliseconds
     * @param {Object=} opt options: {string} interpolation: default: linear; {function} callback at the end of the movement;
     * @return {Moveable} the Moveable
     */
    p.moveTo = function(position, orientation, time, opt){};

    /**
     * Stops the current movement and cancels every queued movement.
     * @return {Moveable} the Moveable
     */
    p.stop = function(){};

    /**
     * Sets a constraint for the Moveable. The constraint is checked
     * @param {Constraint} constraint Set a constraint to the Moveable
     */
    p.setContraint = function(constraint){};

    /**
     * An Animatable
     * @extends Moveable
     * @interface
     */
    var Animatable = function(){};
    var a = Animatable.prototype;

    /**
     * Add an Animation to the Animatable
     * @param {Animation} animation Animation
     * @param {Object=} opt options: {number} duration (in ms); {number} loop; {string} interpolation; {function} callback at the end of the animation;
     * @return {Animatable} the Animatable
     */
    a.addAnimation = function(animation, opt){};

    /**
     * Starts an animation
     * @param {string} name animation, that will be started
     * @param {Object=} opt options: {number} duration (in ms); {number} loop; {string} interpolation; {function} callback at the end of the animation;
     * @return {number} id id of the animation, which was started. The id is valid until the end of the animation
     */
    a.startAnimation = function(name, opt){};

    /**
     * Stops an animation
     * @param {string} id Animation ID
     * @return {Animatable} the Animatable
     */
    a.stopAnimation = function(id){};

    /**
     * Interpolated translation and rotation of a single animation step between two keys
     * The animation step is put into a fifo-queue and will be eventually executed.
     * This is called by the KeyFrameAnimation, but should not be called otherwise, this can be seen as a major design flaw. TODO ???
     * @param {number} id Animation ID
     * @param {Array.<number>|undefined} position local space Vector
     * @param {Array.<number>|undefined} orientation orientation Quaternion
     * @param {number} time when to reach the position, in milliseconds
     * @param {Object=} opt options: {string} interpolation: default: linear; {function} callback at the end of the movement;
     * @return {Moveable} the Moveable
     */
    a.animationStepPose = function(id, position, orientation, time, opt){};

    /**
     * TODO: Interpolated paramater blah...
     */
    r.animationStepParameter = function(id, parameter, time, opt){};

    /**
     * A Animation
     * @interface
     */
    var Animation = function(){};
    var k = Animation.prototype;

    /**
     * Starts the Animation, allows to add additional options for this certain animation.
     * @param {Animatable} animatable the animatable, which will be animated
     * @param {Object=} opt options: {number} duration (in ms); {number} loop; {string} interpolation; {function} callback at the end of the animation;
     */
    k.start = function(animatable, opt){};

	/**
	 * Set Options
	 *{Object} opt options: {string} interpolation: default: linear; {function} callback at the end of the movement;
	 */
	k.setOptions = function(opt){};

	/**
	 * A KeyFrameAnimation
	 * @extends Animation
	 * @interface
	 */
	var KeyFrameAnimation = function(){};

    /**
     * A XFlowAnimation
     * @extends Animation
     * @interface
     */
	var XFlowAnimation = function(){};

	/**
	 * A AnimationCollection
	 * @extends Animation
	 * @interface
	 */
	var CombinedAnimation = function(){};
	var ca = CombinedAnimation.prototype;

	/**
	 * Add an animation to the collection
	 * @param {Animation} animation
	 * @param {Object=} opt
	 */
	ca.addAnimation = function(animation, opt){};

	/**
     * A Constraint
     * @interface
     */
    var Constraint = function(){};
    var c = Constraint.prototype;

    /**
     * Checks if a rotation operation is valid.
     * @param {Array.<number>} rotation Quaternion
     * @param {Moveable} moveable Moveable
     * @return {boolean} returns true if the operation is valid, false otherwise
     */
    c.constrainRotation = function(rotation, moveable){};

    /**
     * Checks if a translation operation is valid.
     * @param {Array.<number>} translation 3d Vector
     * @param {Moveable} moveable Moveable
     * @return {boolean} returns true if the operation is valid, false otherwise
     */
    c.constrainTranslation = function(translation, moveable){};
}());<commit_msg>Small Bugfix<commit_after>/** @namespace **/
//var XMOT = XMOT || {};
goog.provide("XMOT");

/** @define {string} */
XMOT.version = '%VERSION%';

(function() {

    /**
     * A MotionFactory.
     * @interface
     */
    var MotionFactory = function() {};
    var m = MotionFactory.prototype;

    /**
     * Creates a Moveable out of the given object
     * @param {Object} object base for the Moveable
     * @param {Constraint} constraint Constrain movement
     * @return {Moveable} created Moveable
     */
    m.createMoveable = function(object, constraint){};

    /**
     * Creates an Animatable out of the given object
     * @param {Object} object base for the Animatable
     * @param {Constraint} constraint Constrain movement
     * @return {Animatable} created Animatable
     */
    m.createAnimatable = function(object, constraint){};

    /**
     * Creates a KeyframeAnimation
     * @param {string} name name
     * @param {string} type "Position" or "Orientation"
     * @param {Object} element KeyframeAnimation, keyframes and corresponding positions or orientations
     * @param {Object=} opt Options: {number} duration in ms; {number} loop; {string} interpolation; {function} callback at the end of the animation;
     * @return {KeyframeAnimation} created KeyFrameAnimation
     */
    m.createKeyframeAnimation = function(name, type, element, opt){};

    /**
     * A Moveable.
     * @interface
     */
    var Moveable = function() {};
    var p = Moveable.prototype;

    /**
     * Sets the absolute position of the Moveable in local space.
     * @param {Array.<number>} position position as 3d vector in local space
     * @return {Moveable} the Moveable
     */
    p.setPosition = function(position){};

    /**
     * Sets the absolute orientation of the Movebale in local space.
     * @param {Array.<number>} orientation orientation as quaternion in local space
     * @return {Moveable} the Moveable
     */
    p.setOrientation = function(orientation){};

    /**
     * Translate the Moveable by a given Vector.
     * @param {Array.<number>} translation 3d Vector
     * @return {Moveable} the Moveable
     */
    p.translate = function(translation){};

    /**
     * Rotates the Moveable by a given Quaternion.
     * @param {Array.<number>} rotation Quaternion
     * @return {Moveable} the Moveable
     */
    p.rotate = function(rotation){};

    /**
     * Interpolated translation over time to position in local space.
     * The animation is put into a fifo-queue and will be eventually executed.
     * @param {Array.<number>|undefined} position local space Vector
     * @param {Array.<number>|undefined} orientation orientation Quaternion
     * @param {number} time when to reach the position, in milliseconds
     * @param {Object=} opt options: {string} interpolation: default: linear; {function} callback at the end of the movement;
     * @return {Moveable} the Moveable
     */
    p.moveTo = function(position, orientation, time, opt){};

    /**
     * Stops the current movement and cancels every queued movement.
     * @return {Moveable} the Moveable
     */
    p.stop = function(){};

    /**
     * Sets a constraint for the Moveable. The constraint is checked
     * @param {Constraint} constraint Set a constraint to the Moveable
     */
    p.setContraint = function(constraint){};

    /**
     * An Animatable
     * @extends Moveable
     * @interface
     */
    var Animatable = function(){};
    var a = Animatable.prototype;

    /**
     * Add an Animation to the Animatable
     * @param {Animation} animation Animation
     * @param {Object=} opt options: {number} duration (in ms); {number} loop; {string} interpolation; {function} callback at the end of the animation;
     * @return {Animatable} the Animatable
     */
    a.addAnimation = function(animation, opt){};

    /**
     * Starts an animation
     * @param {string} name animation, that will be started
     * @param {Object=} opt options: {number} duration (in ms); {number} loop; {string} interpolation; {function} callback at the end of the animation;
     * @return {number} id id of the animation, which was started. The id is valid until the end of the animation
     */
    a.startAnimation = function(name, opt){};

    /**
     * Stops an animation
     * @param {string} id Animation ID
     * @return {Animatable} the Animatable
     */
    a.stopAnimation = function(id){};

    /**
     * Interpolated translation and rotation of a single animation step between two keys
     * The animation step is put into a fifo-queue and will be eventually executed.
     * This is called by the KeyFrameAnimation, but should not be called otherwise, this can be seen as a major design flaw. TODO ???
     * @param {number} id Animation ID
     * @param {Array.<number>|undefined} position local space Vector
     * @param {Array.<number>|undefined} orientation orientation Quaternion
     * @param {number} time when to reach the position, in milliseconds
     * @param {Object=} opt options: {string} interpolation: default: linear; {function} callback at the end of the movement;
     * @return {Moveable} the Moveable
     */
    a.animationStepPose = function(id, position, orientation, time, opt){};

    /**
     * TODO: Interpolated paramater blah...
     */
    a.animationStepParameter = function(id, parameter, time, opt){};

    /**
     * A Animation
     * @interface
     */
    var Animation = function(){};
    var k = Animation.prototype;

    /**
     * Starts the Animation, allows to add additional options for this certain animation.
     * @param {Animatable} animatable the animatable, which will be animated
     * @param {Object=} opt options: {number} duration (in ms); {number} loop; {string} interpolation; {function} callback at the end of the animation;
     */
    k.start = function(animatable, opt){};

	/**
	 * Set Options
	 *{Object} opt options: {string} interpolation: default: linear; {function} callback at the end of the movement;
	 */
	k.setOptions = function(opt){};

	/**
	 * A KeyFrameAnimation
	 * @extends Animation
	 * @interface
	 */
	var KeyFrameAnimation = function(){};

    /**
     * A XFlowAnimation
     * @extends Animation
     * @interface
     */
	var XFlowAnimation = function(){};

	/**
	 * A AnimationCollection
	 * @extends Animation
	 * @interface
	 */
	var CombinedAnimation = function(){};
	var ca = CombinedAnimation.prototype;

	/**
	 * Add an animation to the collection
	 * @param {Animation} animation
	 * @param {Object=} opt
	 */
	ca.addAnimation = function(animation, opt){};

	/**
     * A Constraint
     * @interface
     */
    var Constraint = function(){};
    var c = Constraint.prototype;

    /**
     * Checks if a rotation operation is valid.
     * @param {Array.<number>} rotation Quaternion
     * @param {Moveable} moveable Moveable
     * @return {boolean} returns true if the operation is valid, false otherwise
     */
    c.constrainRotation = function(rotation, moveable){};

    /**
     * Checks if a translation operation is valid.
     * @param {Array.<number>} translation 3d Vector
     * @param {Moveable} moveable Moveable
     * @return {boolean} returns true if the operation is valid, false otherwise
     */
    c.constrainTranslation = function(translation, moveable){};
}());<|endoftext|>


<commit_before>/**
 * WordPress dependencies
 */
import { deactivatePlugin, activatePlugin, visitAdminPage } from '@wordpress/e2e-test-utils';

describe( 'Reset plugin', () => {
	beforeAll( async() => {
		await activatePlugin( 'e2e-tests-auth-plugin' );
	} );

	afterAll( async() => {
		await deactivatePlugin( 'e2e-tests-auth-plugin' );
	} );

	beforeEach( async() => {
		await visitAdminPage( 'admin.php', 'page=googlesitekit-settings' );
		await expect( page ).toMatchElement( '.googlesitekit-page-header__title', { text: 'Settings' } );

		await page.waitForSelector( 'button.mdc-tab' );

		// Click on Admin Settings Tab.
		await expect( page ).toClick( 'button.mdc-tab', { text: 'Admin Settings' } );
		await page.waitForSelector( '.googlesitekit-settings-module__footer' );
	} );

	it( 'On Reset Site Kit click, confirm dialog displays.', async() => {

		await expect( page ).toClick( 'button.googlesitekit-cta-link', { text: 'Reset Site Kit' } );
		await page.waitForSelector( '.mdc-dialog--open' );

		await expect( page ).toMatchElement( '.mdc-dialog.mdc-dialog--open .mdc-button', { text: 'Reset' } );
	} );

	it( 'On Reset Site Kit click, confirm dialog hides when cancel', async() => {
		await expect( page ).toClick( 'button.googlesitekit-cta-link', { text: 'Reset Site Kit' } );
		await page.waitForSelector( '.mdc-dialog--open' );

		await expect( page ).toClick( '.mdc-dialog.mdc-dialog--open button', { text: 'Cancel' } );
	} );

	it( 'Reset dialog button disconnects site kit', async() => {
		await expect( page ).toClick( 'button.googlesitekit-cta-link', { text: 'Reset Site Kit' } );
		await page.waitForSelector( '.mdc-dialog--open' );
		await expect( page ).toClick( '.mdc-dialog.mdc-dialog--open .mdc-button', { text: 'Reset' } );

		await visitAdminPage( 'admin.php', 'page=googlesitekit-dashboard' );

		await expect( page ).toMatchElement( '.googlesitekit-wizard' );
	} );
} );
<commit_msg>update reset test names for consistency<commit_after>/**
 * WordPress dependencies
 */
import { deactivatePlugin, activatePlugin, visitAdminPage } from '@wordpress/e2e-test-utils';

describe( 'Plugin Reset', () => {
	beforeAll( async() => {
		await activatePlugin( 'e2e-tests-auth-plugin' );
	} );

	afterAll( async() => {
		await deactivatePlugin( 'e2e-tests-auth-plugin' );
	} );

	beforeEach( async() => {
		await visitAdminPage( 'admin.php', 'page=googlesitekit-settings' );
		await expect( page ).toMatchElement( '.googlesitekit-page-header__title', { text: 'Settings' } );

		await page.waitForSelector( 'button.mdc-tab' );

		// Click on Admin Settings Tab.
		await expect( page ).toClick( 'button.mdc-tab', { text: 'Admin Settings' } );
		await page.waitForSelector( '.googlesitekit-settings-module__footer' );
	} );

	it( 'displays a confirmation dialog when clicking the "Reset Site Kit" link', async() => {

		await expect( page ).toClick( 'button.googlesitekit-cta-link', { text: 'Reset Site Kit' } );
		await page.waitForSelector( '.mdc-dialog--open' );

		await expect( page ).toMatchElement( '.mdc-dialog.mdc-dialog--open .mdc-button', { text: 'Reset' } );
	} );

	it( 'dismisses the reset confirmation dialog when clicking "Cancel"', async() => {
		await expect( page ).toClick( 'button.googlesitekit-cta-link', { text: 'Reset Site Kit' } );
		await page.waitForSelector( '.mdc-dialog--open' );

		await expect( page ).toClick( '.mdc-dialog.mdc-dialog--open button', { text: 'Cancel' } );
	} );

	it( 'disconnects Site Kit by clicking the "Reset" button in the confirmation dialog', async() => {
		await expect( page ).toClick( 'button.googlesitekit-cta-link', { text: 'Reset Site Kit' } );
		await page.waitForSelector( '.mdc-dialog--open' );
		await expect( page ).toClick( '.mdc-dialog.mdc-dialog--open .mdc-button', { text: 'Reset' } );

		await visitAdminPage( 'admin.php', 'page=googlesitekit-dashboard' );

		await expect( page ).toMatchElement( '.googlesitekit-wizard' );
	} );
} );
<|endoftext|>


<commit_before>$(document).ready(function() {
  $('.face img').on('click', function(event){
    target = event.target
    // debugger;
    $('.selected').removeClass('selected')
    $(target).parent().addClass('selected');
  })
});<commit_msg>add js to select vibe radio button corresponding with placeholder images<commit_after>$(document).ready(function() {
  $('.face img').on('click', function(event){
    // target = event.target
    selected_div = event.target.parentNode;
    // debugger;
    $('.selected').removeClass('selected')
    $(selected_div).addClass('selected')
    $(selected_div).find('input').prop('checked', true)
  })
});<|endoftext|>


<commit_before>define(function (require, exports, module) {
    'use strict';

    var app = require('app');
    var msgBus = require('msgBus');
    var EquifitEntities = require('entities/equifits');
    var EquifitView = require('views/equifits');
    var HeaderView = require('views/header');
    var BreadcrumbView = require('views/breadcrumb');
    var ActionView = require('views/action');
    var LoadingView = require('views/loading');
    var EquifitsModule = {};

    // create an instance of equifits collection.
    var equifitEntities = new EquifitEntities();

    EquifitsModule.init = function (clientId) {

        app.store.set({
            title: 'Equifits',
            url: '/equifit/client/' + clientId,
            slug: 'equifit',
            clientId: clientId
        });

        app.useLayout('layouts/main').setViews({
            '.main-container': new LoadingView({
                title: 'Loading Equifits'
            })
        }).render();

        // Fetch data and replace loading view
        equifitEntities.fetch().then(
            function () {

                // update Store with member name
                app.store.set({
                    clientName: equifitEntities.at(1).get('clientName')
                });

                app.useLayout('layouts/main').setViews({
                    '.header': new HeaderView(),
                    '.breadcrumb-container': new BreadcrumbView(),
                    '.action-container': new ActionView(),
                    '.main-container': new EquifitView({
                        collection: equifitEntities
                    })
                }).render();

                msgBus.trigger('app:update:title', app.store.get('title'));
            }
        );
    };

    module.exports = EquifitsModule;
});<commit_msg>get index 0 from collection array not index 1<commit_after>define(function (require, exports, module) {
    'use strict';

    var app = require('app');
    var msgBus = require('msgBus');
    var EquifitEntities = require('entities/equifits');
    var EquifitView = require('views/equifits');
    var HeaderView = require('views/header');
    var BreadcrumbView = require('views/breadcrumb');
    var ActionView = require('views/action');
    var LoadingView = require('views/loading');
    var EquifitsModule = {};

    // create an instance of equifits collection.
    var equifitEntities = new EquifitEntities();

    EquifitsModule.init = function (clientId) {

        app.store.set({
            title: 'Equifits',
            url: '/equifit/client/' + clientId,
            slug: 'equifit',
            clientId: clientId
        });

        app.useLayout('layouts/main').setViews({
            '.main-container': new LoadingView({
                title: 'Loading Equifits'
            })
        }).render();

        // Fetch data and replace loading view
        equifitEntities.fetch().then(
            function () {

                // update Store with member name
                app.store.set({
                    clientName: equifitEntities.at(0).get('clientName')
                });

                app.useLayout('layouts/main').setViews({
                    '.header': new HeaderView(),
                    '.breadcrumb-container': new BreadcrumbView(),
                    '.action-container': new ActionView(),
                    '.main-container': new EquifitView({
                        collection: equifitEntities
                    })
                }).render();

                msgBus.trigger('app:update:title', app.store.get('title'));
            }
        );
    };

    module.exports = EquifitsModule;
});<|endoftext|>


<commit_before>import postal from 'postal';

const createPostalAdapter = (channelName = 'reactPubSub') => {
  const channel = postal.channel(channelName);
  return {
    subscribe(action, cb) {
      channel.subscribe(action, cb);
    },
    unsubscribe() {
      channel.unsubscribe();
    },
    publish(action, data) {
      channel.publish(action, ...data);
    },
  };
};

export default createPostalAdapter;
<commit_msg>fix(examples): Aligned custom adapter to latest adapter API<commit_after>import postal from 'postal';

const createPostalAdapter = (channelName = 'reactPubSub') => {
  const channel = postal.channel(channelName);
  return {
    subscribe(action, cb) {
      channel.subscribe(action, cb);
    },
    unsubscribe() {
      channel.unsubscribe();
    },
    publish(action, ...data) {
      channel.publish(action, ...data);
    },
  };
};

export default createPostalAdapter;
<|endoftext|>


<commit_before>// Copyright 2018-2019, University of Colorado Boulder
/* eslint-disable bad-text */

/**
 * Lint detector for invalid text.
 * Lint is disabled for this file so the bad texts aren't themselves flagged.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */

'use strict';

module.exports = function( context ) {

  const getBadTextTester = require( './getBadTextTester' );

  // see getBadTextTester for schema
  const forbiddenTextObjects = [

    // Proper casing for *boxes

    // toolbox is one word
    'toolBox', // prefer toolbox
    'ToolBox', // prefer Toolbox
    'TOOL_BOX', // prefer TOOLBOX

    // checkbox is one word
    'checkBox', // prefer checkbox
    'CheckBox', // prefer Checkbox
    'CHECK_BOX', // prefer CHECKBOX

    // combo box is two words
    'combobox', // prefer combo box
    'Combobox', // prefer Combo Box
    'COMBOBOX', // prefer COMBO_BOX

    // In ES6, extending object causes methods to be dropped
    { id: 'extends Object ', codeTokens: [ 'extends', 'Object' ] },

    // Forbid common duplicate words
    ' the the ',
    ' a a ',
    'dipose', // happens more than you'd think

    // For phet-io use PHET_IO in constants
    'PHETIO',
    'PHET-IO',
    'Phet-iO',
    ' Phet ',
    'phetio element', // use "phet-io element" or "PhET-iO element"
    'Phet-iO',
    { id: 'IO type', regex: /\bIO type/ }, // https://github.com/phetsims/chipper/issues/977

    '@return ',

    'Util = require( \'',// Utils should now be plural, see https://github.com/phetsims/tasks/issues/966

    // if on a one line arrow function returning something, prefer instead `() => theReturn`, see https://github.com/phetsims/chipper/issues/790
    ' => { return ',

    'define( function( require ) {', // use define( require => { to standardize before es6 module migration

    // optional 'options' should use brackets and required 'config' shouldn't use brackets, see https://github.com/phetsims/chipper/issues/859
    '@param {Object} options',
    '@param {Object} [config]',

    'export default inherit(',

    // PhET prefers to use the term "position" to refer to the physical (x,y) position of objects.
    // The lint rule can be disabled for occurrences where we do prefer locationProperty, for instance if we
    // had a sim about people that are from three different named locations.
    'locationProperty',

    {
      id: 'AxonArray({number})',
      regex: /AxonArray\(\s*\d/
    },

    {
      id: 'Import from statements require a *.js suffix',
      predicate: line => {
        if ( line.trim().indexOf( 'import ' ) === 0 && line.indexOf( ' from ' ) > 0 && line.indexOf( '.js' ) === -1 ) {
          return false;
        }
        return true;
      }
    },

    {
      id: 'Import statements require a *.js suffix',
      predicate: line => {
        if ( line.trim().indexOf( 'import \'' ) === 0 && line.indexOf( ';' ) >= 0 && line.indexOf( '.js' ) === -1 ) {
          return false;
        }
        return true;
      }
    },

    {
      id: 'Export statements should not have a register call',
      predicate: line => {
        if ( line.trim().indexOf( 'export default' ) === 0 && line.indexOf( '.register(' ) >= 0 ) {
          return false;
        }
        return true;
      }
    }
  ];

  return {
    Program: getBadTextTester( forbiddenTextObjects, context )
  };
};

module.exports.schema = [
  // JSON Schema for rule options goes here
];<commit_msg>Delete AxonArray, see https://github.com/phetsims/axon/issues/335<commit_after>// Copyright 2018-2019, University of Colorado Boulder
/* eslint-disable bad-text */

/**
 * Lint detector for invalid text.
 * Lint is disabled for this file so the bad texts aren't themselves flagged.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */

'use strict';

module.exports = function( context ) {

  const getBadTextTester = require( './getBadTextTester' );

  // see getBadTextTester for schema
  const forbiddenTextObjects = [

    // Proper casing for *boxes

    // toolbox is one word
    'toolBox', // prefer toolbox
    'ToolBox', // prefer Toolbox
    'TOOL_BOX', // prefer TOOLBOX

    // checkbox is one word
    'checkBox', // prefer checkbox
    'CheckBox', // prefer Checkbox
    'CHECK_BOX', // prefer CHECKBOX

    // combo box is two words
    'combobox', // prefer combo box
    'Combobox', // prefer Combo Box
    'COMBOBOX', // prefer COMBO_BOX

    // In ES6, extending object causes methods to be dropped
    { id: 'extends Object ', codeTokens: [ 'extends', 'Object' ] },

    // Forbid common duplicate words
    ' the the ',
    ' a a ',
    'dipose', // happens more than you'd think

    // For phet-io use PHET_IO in constants
    'PHETIO',
    'PHET-IO',
    'Phet-iO',
    ' Phet ',
    'phetio element', // use "phet-io element" or "PhET-iO element"
    'Phet-iO',
    { id: 'IO type', regex: /\bIO type/ }, // https://github.com/phetsims/chipper/issues/977

    '@return ',

    'Util = require( \'',// Utils should now be plural, see https://github.com/phetsims/tasks/issues/966

    // if on a one line arrow function returning something, prefer instead `() => theReturn`, see https://github.com/phetsims/chipper/issues/790
    ' => { return ',

    'define( function( require ) {', // use define( require => { to standardize before es6 module migration

    // optional 'options' should use brackets and required 'config' shouldn't use brackets, see https://github.com/phetsims/chipper/issues/859
    '@param {Object} options',
    '@param {Object} [config]',

    'export default inherit(',

    // PhET prefers to use the term "position" to refer to the physical (x,y) position of objects.
    // The lint rule can be disabled for occurrences where we do prefer locationProperty, for instance if we
    // had a sim about people that are from three different named locations.
    'locationProperty',

    {
      id: 'Import from statements require a *.js suffix',
      predicate: line => {
        if ( line.trim().indexOf( 'import ' ) === 0 && line.indexOf( ' from ' ) > 0 && line.indexOf( '.js' ) === -1 ) {
          return false;
        }
        return true;
      }
    },

    {
      id: 'Import statements require a *.js suffix',
      predicate: line => {
        if ( line.trim().indexOf( 'import \'' ) === 0 && line.indexOf( ';' ) >= 0 && line.indexOf( '.js' ) === -1 ) {
          return false;
        }
        return true;
      }
    },

    {
      id: 'Export statements should not have a register call',
      predicate: line => {
        if ( line.trim().indexOf( 'export default' ) === 0 && line.indexOf( '.register(' ) >= 0 ) {
          return false;
        }
        return true;
      }
    }
  ];

  return {
    Program: getBadTextTester( forbiddenTextObjects, context )
  };
};

module.exports.schema = [
  // JSON Schema for rule options goes here
];<|endoftext|>


<commit_before>'use strict';
var app = app || {};

(function(module) {
  const resultsController = {};

  resultsController.initResultsView = function() {
    $('.tab-content').hide();
    $('#results').show();
    var button = $('<button>Watched!</button>').attr('id', 'watchedButton');
    $('#results').append(button);
    $('#watchedButton').on('click', function(){
      event.preventDefault();
      app.watchedMovie();
      app.selectMovie();
      $('#results').empty();
      app.displayMovie(app.movieSelection);
      var button = $('<button>Watched!</button>').attr('id', 'watchedButton');
      $('#results').append(button);
    })
  }

  module.resultsController = resultsController;
})(app);
<commit_msg>added recursive button handler<commit_after>'use strict';
var app = app || {};

(function(module) {
  const resultsController = {};

  resultsController.cycleResultsView = function() {
    $('#results').empty();
    var button = $('<button>Watched!</button>').attr('id', 'watchedButton');
    $('#results').append(button);
    app.selectMovie();
    app.displayMovie(app.movieSelection);

    $('#watchedButton').on('click', function(){
      event.preventDefault();
      resultsController.cycleResultsView();
    })
  }

  resultsController.initResultsView = function() {
    $('.tab-content').hide();
    $('#results').show();

    resultsController.cycleResultsView();
  }

  module.resultsController = resultsController;
})(app);
<|endoftext|>


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

MODEL({
  name: 'DocPropertiesView',
  extendsModel: 'View',
  help: 'Displays the documentation of the given Properties.',

  properties: [
    {
      name:  'data',
      help: 'The model whose properties to view.',
      postSet: function(old, data) {
        if (old) Events.unfollow(old.properties$, this.dao$);
        Events.follow(data.properties$, this.dao$);
        this.updateHTML();
      }
    },
    {
      name:  'dao',
      model_: 'DAOProperty',
      postSet: function() {
        this.filteredDAO = this.dao.where(EQ(Property.HIDDEN, FALSE));

        this.filteredDAO.select(COUNT())(function(c) {
          this.isEmpty = c.count <= 0;
        }.bind(this));

      }
    },
    {
      name:  'filteredDAO',
      model_: 'DAOProperty'
    },
    {
      name: 'isEmpty',
      defaultValue: true,
      postSet: function(_, nu) {
        this.updateHTML();
      }
    }
  ],

  templates: [
    function toInnerHTML()    {/*
    <%    this.destroy();
          if (this.isEmpty) { %>

No Properties.

    <%    } else { %>

Properties:

$$filteredDAO{ model_: 'DAOListView', rowView: 'DocPropertyRowView', data: this.filteredDAO, model: Property }

    <%    } %>
    */}
  ],

});

MODEL({
  name: 'DocPropertyRowView',
  extendsModel: 'DocBodyView',

  templates: [
    function toInnerHTML() {/*

<%=this.data.name%>

      <%=this.renderDocSourceHTML()%>
    */}
  ]
});

MODEL({
  name: 'DocBodyView',
  extendsModel: 'View',
  label: 'Documentation Body View Base',
  help: 'Base Model for documentation body-text views.',

  properties: [
    {
      name: 'data',
      help: 'The model or feature from which to extract documentation.',
      required: true,
      preSet: function(old, nu) {
        if (old && Documentation.isInstance(old.documentation)) {
          Events.unfollow(old.documentation$, this.docSource$);
        }
        return nu;
      },
      postSet: function() {
        // grab the documentation and compile a template to use in this view
        if (this.data && Documentation.isInstance(this.data.documentation)) {
          // bind docSource
          Events.follow(this.data.documentation$, this.docSource$);
        }
      }
    },
    {
      name: 'docSource',
      type: 'Documentation',
      help: 'The documentation object to render.',
      postSet: function() {
        this.updateHTML();
      }
    },
  ],

  methods: {
    init: function() {
      this.SUPER();
      if (!this.X.documentViewParentModel) {
        console.log("*** Warning: DocView ",this," can't find documentViewParentModel in its context "+this.X.NAME);
      }
    },

    renderDocSourceHTML: function() {
      // only update if we have all required data
      if (this.docSource.body && this.X.documentViewParentModel
          && this.X.documentViewParentModel.model_ && this.data.model_) {
        // The first time this method is hit, replace it with the one that will
        // compile the template, then call that. Future calls go direct to lazyCompile's
        // returned function. You could also implement this the same way lazyCompile does...
        this.renderDocSourceHTML = TemplateUtil.lazyCompile(this.docSource.body);
        return this.renderDocSourceHTML();
      } else {
        return ""; // no data yet
      }
    },

    /** Create the special reference lookup sub-view from property info. **/
    createReferenceView: function(opt_args) {
      var X = ( opt_args && opt_args.X ) || this.X; // TODO: opt_args should have ref and text auto-set on the view?
      var v = X.DocRefView.create({ ref:opt_args.ref, text: opt_args.text, args: opt_args});
      this.addChild(v);
      return v;
    },

    createTemplateView: function(name, opt_args) {
      // name has been constantized ('PROP_NAME'), but we're
      // only looking for certain doc tags anyway.
      if (name === 'DOC') {
        var v = this.createReferenceView(opt_args);
        return v;
      } else {
        return this.SUPER(name, opt_args);
      }
    }
  }
});

MODEL({
  name: 'DocModelBodyView',
  extendsModel: 'DocBodyView',
  label: 'Documentation Model View',
  help: 'Shows the documentation body for a Model.',

  properties: [
    {
      name: 'data',
      help: 'The Model from which to extract documentation.',
      postSet: function() {
        this.X[this.model_.extendsModel].DATA.postSet.call(this); // TODO: implement this.SUPER() for these
        this.parentModel = this.data;
      }
    },

  ],

  templates: [
    function toInnerHTML()    {/*
      <%    this.destroy(); %>
      <%    if (this.data) {  %>

<%=this.data.name%>

              <%=this.renderDocSourceHTML()%>
      <%    } %>
    */}
  ],

});

MODEL({
  name: 'DocFeatureBodyView',
  extendsModel: 'DocBodyView',
  label: 'Documentation Feature View',
  help: 'Shows documentation body for a feature of a Model.',

  // You must set both data and parentModel

  templates: [
    function toInnerHTML()    {/*
      <%    this.destroy(); %>
      <%    if (this.data) {  %>

<%=this.data.name%>

              <%=this.renderDocSourceHTML()%>
      <%    } %>
    */}
  ]
});

MODEL({
  name: 'DocRefView',
  extendsModel: 'View',
  label: 'Documentation Reference View',
  help: 'The view of a documentation reference link.',

  properties: [
    {
      name: 'data',
      help: 'The referenced object.',
      postSet: function() {
        this.updateHTML();
      }
    },
    {
      name: 'ref',
      help: 'The reference to link. Must be of the form "Model", "Model.feature", or ".feature"',
      postSet: function() {
        this.data = this.resolveReference(this.ref);
      }
    },
    {
      name: 'text',
      help: 'Text to display instead of the referenced object's default label or name.'
    }

  ],

  templates: [
    // kept tight to avoid HTML adding whitespace around it
    function toInnerHTML()    {/*<%
       this.destroy();
      if (this.text && this.text.length > 0) {
        %>[<%=this.text%>]<%
      } else if (this.data && this.data.label && this.data.label.length > 0) {
        %>[<%=this.data.label%>]<%
      } else if (this.data && this.data.name) {
        %>[<%=this.data.name%>]<%
      } else if (this.data.id) {
        %>[<%=this.data.id%>]<%
      } else {
        %>[INVALID_REF:<%=this.ref%>]<%
      } %>*/}
  ],

  methods: {
    init: function() {
      this.SUPER();

      this.tagName = 'span';

      if (!this.X.documentViewParentModel) {
        console.log("*** Warning: DocView ",this," can't find documentViewParentModel in its context "+this.X.NAME);
        debugger;
      } else {
        this.X.documentViewParentModel.addListener(this.onParentModelChanged);
      }
    },

    resolveReference: function(reference) {
      // TODO: method arguments can be referenced too? Model.methodName.args.arg1

      // parse "Model.feature" or "Model" or ".feature" with implicit Model==this.data
      args = reference.split('.');
      var foundObject;
      var model;

      // if model not specified, use parentModel
      if (args[0].length <= 0) {
        if (!this.X.documentViewParentModel) return foundObject; // abort
        model = this.X.documentViewParentModel.get();
      } else {
        model = this.X[args[0]];
      }

      if (model && args.length > 1 && args[1].length > 0)
      {
        // feature specified
        foundObject = model.getFeature(args[1]);
      } else {
        // no feature found, so assume it's a model
        foundObject = model;
      }

      // allow further specification of sub properties or lists
      if (foundObject && args.length > 2)
      {
        remainingArgs = args.slice(2);

        remainingArgs.some(function (arg) {
          var newObject;

          // null arg is an error at this point
          if (arg.length <= 0) return false;

          // check if arg is the name of a sub-object of foundObject
          var argCaller = Function('return this.'+arg);
          if (argCaller.call(foundObject)) {
            newObject = argCaller.call(foundObject);
          } else if (foundObject.mapFind) {
            // otherwise if foundObject is a list, check names of each thing inside
            foundObject.mapFind(function(f) {
              if (f.name && f.name === arg && f) {
                newObject = f;
              }
            })
          }
          foundObject = newObject; // will reset to undefined if we failed to resolve the latest part
          if (!foundObject) return false;
        });
      }
      return foundObject;
    },
  },

  listeners: [
    {
        name: 'onParentModelChanged',
        code: function() {
          this.data = this.resolveReference(this.ref);
        }
    }
  ]

});

<commit_msg>Fixed dupe title in DocModelBodyView<commit_after>/**
 * @license
 * Copyright 2013 Google Inc. All Rights Reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

MODEL({
  name: 'DocPropertiesView',
  extendsModel: 'View',
  help: 'Displays the documentation of the given Properties.',

  properties: [
    {
      name:  'data',
      help: 'The model whose properties to view.',
      postSet: function(old, data) {
        if (old) Events.unfollow(old.properties$, this.dao$);
        Events.follow(data.properties$, this.dao$);
        this.updateHTML();
      }
    },
    {
      name:  'dao',
      model_: 'DAOProperty',
      postSet: function() {
        this.filteredDAO = this.dao.where(EQ(Property.HIDDEN, FALSE));

        this.filteredDAO.select(COUNT())(function(c) {
          this.isEmpty = c.count <= 0;
        }.bind(this));

      }
    },
    {
      name:  'filteredDAO',
      model_: 'DAOProperty'
    },
    {
      name: 'isEmpty',
      defaultValue: true,
      postSet: function(_, nu) {
        this.updateHTML();
      }
    }
  ],

  templates: [
    function toInnerHTML()    {/*
    <%    this.destroy();
          if (this.isEmpty) { %>

No Properties.

    <%    } else { %>

Properties:

$$filteredDAO{ model_: 'DAOListView', rowView: 'DocPropertyRowView', data: this.filteredDAO, model: Property }

    <%    } %>
    */}
  ],

});

MODEL({
  name: 'DocPropertyRowView',
  extendsModel: 'DocBodyView',

  templates: [
    function toInnerHTML() {/*

<%=this.data.name%>

      <%=this.renderDocSourceHTML()%>
    */}
  ]
});

MODEL({
  name: 'DocBodyView',
  extendsModel: 'View',
  label: 'Documentation Body View Base',
  help: 'Base Model for documentation body-text views.',

  properties: [
    {
      name: 'data',
      help: 'The model or feature from which to extract documentation.',
      required: true,
      preSet: function(old, nu) {
        if (old && Documentation.isInstance(old.documentation)) {
          Events.unfollow(old.documentation$, this.docSource$);
        }
        return nu;
      },
      postSet: function() {
        // grab the documentation and compile a template to use in this view
        if (this.data && Documentation.isInstance(this.data.documentation)) {
          // bind docSource
          Events.follow(this.data.documentation$, this.docSource$);
        }
      }
    },
    {
      name: 'docSource',
      type: 'Documentation',
      help: 'The documentation object to render.',
      postSet: function() {
        this.updateHTML();
      }
    },
  ],

  methods: {
    init: function() {
      this.SUPER();
      if (!this.X.documentViewParentModel) {
        console.log("*** Warning: DocView ",this," can't find documentViewParentModel in its context "+this.X.NAME);
      }
    },

    renderDocSourceHTML: function() {
      // only update if we have all required data
      if (this.docSource.body && this.X.documentViewParentModel
          && this.X.documentViewParentModel.model_ && this.data.model_) {
        // The first time this method is hit, replace it with the one that will
        // compile the template, then call that. Future calls go direct to lazyCompile's
        // returned function. You could also implement this the same way lazyCompile does...
        this.renderDocSourceHTML = TemplateUtil.lazyCompile(this.docSource.body);
        return this.renderDocSourceHTML();
      } else {
        return ""; // no data yet
      }
    },

    /** Create the special reference lookup sub-view from property info. **/
    createReferenceView: function(opt_args) {
      var X = ( opt_args && opt_args.X ) || this.X; // TODO: opt_args should have ref and text auto-set on the view?
      var v = X.DocRefView.create({ ref:opt_args.ref, text: opt_args.text, args: opt_args});
      this.addChild(v);
      return v;
    },

    createTemplateView: function(name, opt_args) {
      // name has been constantized ('PROP_NAME'), but we're
      // only looking for certain doc tags anyway.
      if (name === 'DOC') {
        var v = this.createReferenceView(opt_args);
        return v;
      } else {
        return this.SUPER(name, opt_args);
      }
    }
  }
});

MODEL({
  name: 'DocModelBodyView',
  extendsModel: 'DocBodyView',
  label: 'Documentation Model View',
  help: 'Shows the documentation body for a Model.',

  properties: [
    {
      name: 'data',
      help: 'The Model from which to extract documentation.',
      postSet: function() {
        this.X[this.model_.extendsModel].DATA.postSet.call(this); // TODO: implement this.SUPER() for these
        this.parentModel = this.data;
      }
    },

  ],

  templates: [
    function toInnerHTML()    {/*
      <%    this.destroy(); %>
      <%    if (this.data) {  %>
              <%=this.renderDocSourceHTML()%>
      <%    } %>
    */}
  ],

});

MODEL({
  name: 'DocFeatureBodyView',
  extendsModel: 'DocBodyView',
  label: 'Documentation Feature View',
  help: 'Shows documentation body for a feature of a Model.',

  // You must set both data and parentModel

  templates: [
    function toInnerHTML()    {/*
      <%    this.destroy(); %>
      <%    if (this.data) {  %>

<%=this.data.name%>

              <%=this.renderDocSourceHTML()%>
      <%    } %>
    */}
  ]
});

MODEL({
  name: 'DocRefView',
  extendsModel: 'View',
  label: 'Documentation Reference View',
  help: 'The view of a documentation reference link.',

  properties: [
    {
      name: 'data',
      help: 'The referenced object.',
      postSet: function() {
        this.updateHTML();
      }
    },
    {
      name: 'ref',
      help: 'The reference to link. Must be of the form "Model", "Model.feature", or ".feature"',
      postSet: function() {
        this.data = this.resolveReference(this.ref);
      }
    },
    {
      name: 'text',
      help: 'Text to display instead of the referenced object's default label or name.'
    }

  ],

  templates: [
    // kept tight to avoid HTML adding whitespace around it
    function toInnerHTML()    {/*<%
       this.destroy();
      if (this.text && this.text.length > 0) {
        %>[<%=this.text%>]<%
      } else if (this.data && this.data.label && this.data.label.length > 0) {
        %>[<%=this.data.label%>]<%
      } else if (this.data && this.data.name) {
        %>[<%=this.data.name%>]<%
      } else if (this.data.id) {
        %>[<%=this.data.id%>]<%
      } else {
        %>[INVALID_REF:<%=this.ref%>]<%
      } %>*/}
  ],

  methods: {
    init: function() {
      this.SUPER();

      this.tagName = 'span';

      if (!this.X.documentViewParentModel) {
        console.log("*** Warning: DocView ",this," can't find documentViewParentModel in its context "+this.X.NAME);
        debugger;
      } else {
        this.X.documentViewParentModel.addListener(this.onParentModelChanged);
      }
    },

    resolveReference: function(reference) {
      // TODO: method arguments can be referenced too? Model.methodName.args.arg1

      // parse "Model.feature" or "Model" or ".feature" with implicit Model==this.data
      args = reference.split('.');
      var foundObject;
      var model;

      // if model not specified, use parentModel
      if (args[0].length <= 0) {
        if (!this.X.documentViewParentModel) return foundObject; // abort
        model = this.X.documentViewParentModel.get();
      } else {
        model = this.X[args[0]];
      }

      if (model && args.length > 1 && args[1].length > 0)
      {
        // feature specified
        foundObject = model.getFeature(args[1]);
      } else {
        // no feature found, so assume it's a model
        foundObject = model;
      }

      // allow further specification of sub properties or lists
      if (foundObject && args.length > 2)
      {
        remainingArgs = args.slice(2);

        remainingArgs.some(function (arg) {
          var newObject;

          // null arg is an error at this point
          if (arg.length <= 0) return false;

          // check if arg is the name of a sub-object of foundObject
          var argCaller = Function('return this.'+arg);
          if (argCaller.call(foundObject)) {
            newObject = argCaller.call(foundObject);
          } else if (foundObject.mapFind) {
            // otherwise if foundObject is a list, check names of each thing inside
            foundObject.mapFind(function(f) {
              if (f.name && f.name === arg && f) {
                newObject = f;
              }
            })
          }
          foundObject = newObject; // will reset to undefined if we failed to resolve the latest part
          if (!foundObject) return false;
        });
      }
      return foundObject;
    },
  },

  listeners: [
    {
        name: 'onParentModelChanged',
        code: function() {
          this.data = this.resolveReference(this.ref);
        }
    }
  ]

});

<|endoftext|>


<commit_before>(function( global, $ ) {
  var plugins = {},
      MAX_IMAGE_WIDTH = 1280,
      MAX_IMAGE_HEIGHT = 740;

  var EditorHelper = function() {
    throw "Do not use EditorHelper in this manner. Use EditorHelper.init instead.";
  };

  // This fix is to ensure content-editable still updates correctly, and deals with ie9 not reading document.activeElement properly
  function blurActiveEl() {
   if ( document.activeElement && document.activeElement.blur ) {
      document.activeElement.blur();
    }
  }

  function calculateFinalPositions( event, ui, trackEvent, targetContainer, container, options ) {
    var target = targetContainer.getBoundingClientRect(),
        cont = container.getBoundingClientRect(),
        height = cont.height,
        width = cont.width,
        top = ui.position.top,
        left = ui.position.left,
        targetHeight = target.height,
        targetWidth = target.width,
        minHeightPix = targetHeight * ( ( options.minHeight || 0 ) / 100 ),
        minWidthPix = targetWidth * ( ( options.minWidth || 0 ) / 100 );

    top = Math.max( 0, top );
    left = Math.max( 0, left );
    height = Math.max( minHeightPix, height );
    width = Math.max( minWidthPix, width );

    if ( ( container.offsetTop + height ) > targetHeight ) {
      top = targetHeight - height;
    }

    if ( ( container.offsetLeft + width ) > targetWidth ) {
      left = targetWidth - width;
    }

    height = ( height / targetHeight ) * 100;
    width = ( width / targetWidth ) * 100;

    if ( options.end ) {
      options.end();
    }

    blurActiveEl();

    trackEvent.update({
      height: height,
      width: width,
      top: ( top / targetHeight ) * 100,
      left: ( left / targetWidth ) * 100
    });
  }

  EditorHelper.init = function( butter ) {

    /**
     * Member: draggable
     *
     * Makes a container draggable using jQueryUI
     *
     * @param {TrackEvent} trackEvent: The trackEvent to update when position changes
     * @param {DOMElement} dragContainer: the container which to apply draggable to
     * @param {media} The current media's target element in Butter ( parent container )
     * @param {Object} extra options to apply to the draggable call
     *                 Options are:
     *                    {DOMElement} handle: Restrict drag start event to this element
     *                    {Function} start: Function to execute on drag start event
     *                    {Function} end: Fucntion to execute on drag end event
     */
    global.EditorHelper.draggable = function( trackEvent, dragContainer, targetContainer, options ) {
      if ( $( dragContainer ).data( "draggable" ) ) {
        return;
      }

      var iframeCover = targetContainer.querySelector( ".butter-iframe-fix" );

      options = options || {};

      function createHelper( suffix ) {
        var el = document.createElement( "div" );
        el.classList.add( "ui-draggable-handle" );
        el.classList.add( "ui-draggable-" + suffix );
        return el;
      }

      dragContainer.appendChild( createHelper( "top" ) );
      dragContainer.appendChild( createHelper( "bottom" ) );
      dragContainer.appendChild( createHelper( "left" ) );
      dragContainer.appendChild( createHelper( "right" ) );
      dragContainer.appendChild( createHelper( "grip" ) );

      $( dragContainer ).draggable({
        handle: ".ui-draggable-handle",
        containment: "parent",
        start: function() {
          iframeCover.style.display = "block";

          // Open the editor
          butter.editor.editTrackEvent( trackEvent );

          if ( options.start ) {
            options.start();
          }
        },
        stop: function( event, ui ) {
          iframeCover.style.display = "none";

          calculateFinalPositions( event, ui, trackEvent, targetContainer, dragContainer, options );
        }
      });
    };

    /**
     * Member: resizable
     *
     * Makes a container resizable using jQueryUI
     *
     * @param {TrackEvent} trackEvent: The trackEvent to update when size changes
     * @param {DOMElement} resizeContainer: the container which to apply resizable to
     * @param {media} The current media's target element in Butter ( parent container )
     * @param {Object} extra options to apply to the resizeable call
     *                 Options are:
     *                    {String} handlePositions: describes where to position resize handles ( i.e. "n,s,e,w" )
     *                              - Recommended that this option is specified due to a bug in z-indexing with
     *                                jQueryUI Resizable.
     *                    {Function} start: Function to execute on resize start event
     *                    {Function} end: Fucntion to execute on resize end event
     *                    {Number} minWidth: Minimum width that the resizeContainer should be
     *                    {Number} minHeight: Minimum height that the resizeContainer should be
     */
    global.EditorHelper.resizable = function( trackEvent, resizeContainer, targetContainer, options ) {
      if ( $( resizeContainer ).data( "resizable" ) ) {
        return;
      }

      var iframeCover = targetContainer.querySelector( ".butter-iframe-fix" );

      options = options || {};

      $( resizeContainer ).resizable({
        handles: options.handlePositions,
        start: function() {
          iframeCover.style.display = "block";

          // Open the editor
          butter.editor.editTrackEvent( trackEvent );

          if ( options.start ) {
            options.start();
          }
        },
        containment: "parent",
        stop: function( event, ui ) {
          iframeCover.style.display = "none";

          calculateFinalPositions( event, ui, trackEvent, targetContainer, resizeContainer, options );
        }
      });
    };

    /**
     * Member: contentEditable
     *
     * Makes a container's content editable using contenteditable
     *
     * @param {TrackEvent} trackEvent: The trackEvent to update when content changes
     * @param {DOMElement} contentContainer: the container which to listen for changes and set as editable
     */
    global.EditorHelper.contentEditable = function( trackEvent, contentContainers ) {
      var newText = "",
          contentContainer,
          updateText,
          updateTrackEvent,
          onBlur,
          onKeyDown,
          onMouseDown;

      updateText = function() {
        newText = "";
        for ( var i = 0, l = contentContainers.length; i < l; i++ ) {
          contentContainer = contentContainers[ i ];
          contentContainer.innerHTML = contentContainer.innerHTML.replace( /<br>/g, "\n" );
          newText += contentContainer.textContent;
          if ( i < l - 1 ) {
            newText += "\n";
          }
        }
      };
      updateTrackEvent = function() {
        blurActiveEl();
        trackEvent.update({
          text: newText
        });
      };
      onBlur = function() {
        // store the new text.
        updateText();
        // update the text after any existing events are done.
        // this way we do not revert any other event's changes.
        setTimeout( updateTrackEvent, 0 );
      };
      onKeyDown = function( e ) {
        // enter key for an update.
        // shift + enter for newline.
        if ( !e.shiftKey && e.keyCode === 13 ) {
          updateText();
          updateTrackEvent();
        }
      };
      onMouseDown = function( e ) {
        e.stopPropagation();

        // Open the editor
        butter.editor.editTrackEvent( trackEvent );

        $( contentContainer ).draggable( "destroy" );
      };

      for ( var i = 0, l = contentContainers.length; i < l; i++ ) {
        contentContainer = contentContainers[ i ];
        if ( contentContainer ) {
          contentContainer.addEventListener( "blur", onBlur, false );
          contentContainer.addEventListener( "keydown", onKeyDown, false );
          contentContainer.addEventListener( "mousedown", onMouseDown, false );
          contentContainer.setAttribute( "contenteditable", "true" );
        }
      }
    };

    var mimeTypeMapping = {
      "image/gif": "image/png",
      "image/jpeg": "image/jpeg",
      "image/png": "image/png"
    };

    /**
     * Member: droppable
     *
     * Make a container listen for drop events for loading images from a local machine
     *
     * @param {TrackEvent} trackEvent: The trackEvent to update when content changes
     * @param {DOMElement} dropContainer: The container that listens for the drop events
     */

    global.EditorHelper.droppable = function( trackEvent, dropContainer ) {
      dropContainer.addEventListener( "dragover", function( e ) {
        e.preventDefault();
        dropContainer.classList.add( "butter-dragover" );
      }, false );

      dropContainer.addEventListener( "dragleave", function( e ) {
        e.preventDefault();
        dropContainer.classList.remove( "butter-dragover" );
      }, false );

      dropContainer.addEventListener( "mousedown", function( e ) {
        // Prevent being able to drag the images inside and re drop them
        e.preventDefault();
      }, false );

      dropContainer.addEventListener( "drop", function( e ) {
        var file, imgSrc, imgURI, image, div;

        e.preventDefault();
        e.stopPropagation();

        dropContainer.classList.remove( "butter-dragover" );

        if ( !e.dataTransfer || !e.dataTransfer.files || !e.dataTransfer.files[ 0 ] ) {
          butter.dispatch( "droppable-unsupported" );
          return;
        }

        file = e.dataTransfer.files[ 0 ];

        // If we can't reasonably convert whatever they're dropping on us then bail
        var exportAs = mimeTypeMapping[ file.type ];
        if ( !exportAs ) {
          butter.dispatch( "filetype-unsupported" );
          return;
        }

        if ( window.URL && window.URL.createObjectURL ) {
          imgSrc = window.URL.createObjectURL( file );
        } else if ( window.webkitURL && window.webkitURL.createObjectURL ) {
          imgSrc = window.webkitURL.createObjectURL( file );
        } else {
          butter.dispatch( "droppable-unsupported" );
        }

        // So yeah, Opera has the functionality namespaced but all it currently does is:
        // window.URL.createObjectURL = function(obj) {
        //   return obj;
        // };
        if ( imgSrc === file ) {
          butter.dispatch( "droppable-unsupported" );
          return;
        } else {

          image = document.createElement( "img" );
          image.onload = function() {
            var width = this.width,
                height = this.height,
                wRatio, hRatio, resizeRatio,
                scaledWidth, scaledHeight,
                canvas = document.createElement( "canvas" ),
                context;

            // Fit image nicely into our largest embed size, using
            // the longest side and aspect ratio. Inspired by:
            // http://stackoverflow.com/questions/7863653/algorithm-to-resize-image-and-maintain-aspect-ratio-to-fit-iphone
            if ( width >= height ) {
              if ( width <= MAX_IMAGE_WIDTH && height <= MAX_IMAGE_HEIGHT ) {
                scaledWidth = width;
                scaledHeight = height;
              } else {
                wRatio = MAX_IMAGE_WIDTH / width;
                hRatio = MAX_IMAGE_HEIGHT / height;
                resizeRatio = Math.min( wRatio, hRatio );
                scaledHeight = height * resizeRatio;
                scaledWidth = width * resizeRatio;
              }
            } else {
              if ( height <= MAX_IMAGE_WIDTH && width <= MAX_IMAGE_HEIGHT ) {
                scaledWidth = width;
                scaledHeight = height;
              } else {
                wRatio = MAX_IMAGE_HEIGHT / width;
                hRatio = MAX_IMAGE_WIDTH / height;
                resizeRatio = Math.min( wRatio, hRatio );
                scaledHeight = height * resizeRatio;
                scaledWidth = width * resizeRatio;
              }
            }

            canvas.width = scaledWidth;
            canvas.height = scaledHeight;
            context = canvas.getContext( "2d" );
            context.drawImage( this, 0, 0, scaledWidth, scaledHeight );

            imgURI = canvas.toDataURL( exportAs );

            // If the browser's canvas impl. doesn't support whatever we requested then bail
            if ( imgURI.indexOf( exportAs ) === -1 ) {
              butter.dispatch( "droppable-unsupported" );
              return;
            }

            trackEvent.update( { src: imgURI } );

            if ( window.URL && window.URL.revokeObjectURL ) {
              window.URL.revokeObjectURL( imgSrc );
            } else if ( window.webkitURL && window.webkitURL.revokeObjectURL ) {
              window.webkitURL.revokeObjectURL( imgSrc );
            }
          };
          image.src = imgSrc;

          butter.editor.editTrackEvent( trackEvent );

          // Force image to download, esp. Opera. We can't use
          // "display: none", since that makes it invisible, and
          // thus not load.  Opera also requires the image be
          // in the DOM before it will load.
          div = document.createElement( "div" );
          div.setAttribute( "data-butter-exclude", "true" );
          div.className = "butter-image-preload";
          div.appendChild( image );
          document.body.appendChild( div );
        }
      }, false );
    };

    function _updateFunction( e ) {

      var trackEvent = e.target;

      if ( trackEvent.popcornTrackEvent && plugins[ trackEvent.type ] ) {
        plugins[ trackEvent.type ]( trackEvent, butter.currentMedia.popcorn.popcorn );
      }
    } //updateFunction

    butter.listen( "trackeventupdated", _updateFunction );
  };

  EditorHelper.addPlugin = function( plugin, callback ) {
    plugins[ plugin ] = callback;
  };

  global.EditorHelper = EditorHelper;

}( window, window.jQuery ));
<commit_msg>[#858715] Enforce drag n drop sizes before the update.<commit_after>(function( global, $ ) {
  var plugins = {},
      MAX_IMAGE_WIDTH = 1280,
      MAX_IMAGE_HEIGHT = 740;

  var EditorHelper = function() {
    throw "Do not use EditorHelper in this manner. Use EditorHelper.init instead.";
  };

  // This fix is to ensure content-editable still updates correctly, and deals with ie9 not reading document.activeElement properly
  function blurActiveEl() {
   if ( document.activeElement && document.activeElement.blur ) {
      document.activeElement.blur();
    }
  }

  function calculateFinalPositions( event, ui, trackEvent, targetContainer, container, options ) {
    var target = targetContainer.getBoundingClientRect(),
        cont = container.getBoundingClientRect(),
        height = cont.height,
        width = cont.width,
        top = ui.position.top,
        left = ui.position.left,
        targetHeight = target.height,
        targetWidth = target.width,
        minHeightPix = targetHeight * ( ( options.minHeight || 0 ) / 100 ),
        minWidthPix = targetWidth * ( ( options.minWidth || 0 ) / 100 );

    top = Math.max( 0, top );
    left = Math.max( 0, left );
    height = Math.max( minHeightPix, height );
    width = Math.max( minWidthPix, width );

    if ( ( container.offsetTop + height ) > targetHeight ) {
      top = targetHeight - height;
    }

    if ( ( container.offsetLeft + width ) > targetWidth ) {
      left = targetWidth - width;
    }

    height = ( height / targetHeight ) * 100;
    width = ( width / targetWidth ) * 100;

    if ( options.end ) {
      options.end();
    }

    // Enforce container size here, instead of relying on the update.
    container.style.width = width + "%";
    container.style.height = height + "%";

    blurActiveEl();

    trackEvent.update({
      height: height,
      width: width,
      top: ( top / targetHeight ) * 100,
      left: ( left / targetWidth ) * 100
    });
  }

  EditorHelper.init = function( butter ) {

    /**
     * Member: draggable
     *
     * Makes a container draggable using jQueryUI
     *
     * @param {TrackEvent} trackEvent: The trackEvent to update when position changes
     * @param {DOMElement} dragContainer: the container which to apply draggable to
     * @param {media} The current media's target element in Butter ( parent container )
     * @param {Object} extra options to apply to the draggable call
     *                 Options are:
     *                    {DOMElement} handle: Restrict drag start event to this element
     *                    {Function} start: Function to execute on drag start event
     *                    {Function} end: Fucntion to execute on drag end event
     */
    global.EditorHelper.draggable = function( trackEvent, dragContainer, targetContainer, options ) {
      if ( $( dragContainer ).data( "draggable" ) ) {
        return;
      }

      var iframeCover = targetContainer.querySelector( ".butter-iframe-fix" );

      options = options || {};

      function createHelper( suffix ) {
        var el = document.createElement( "div" );
        el.classList.add( "ui-draggable-handle" );
        el.classList.add( "ui-draggable-" + suffix );
        return el;
      }

      dragContainer.appendChild( createHelper( "top" ) );
      dragContainer.appendChild( createHelper( "bottom" ) );
      dragContainer.appendChild( createHelper( "left" ) );
      dragContainer.appendChild( createHelper( "right" ) );
      dragContainer.appendChild( createHelper( "grip" ) );

      $( dragContainer ).draggable({
        handle: ".ui-draggable-handle",
        containment: "parent",
        start: function() {
          iframeCover.style.display = "block";

          // Open the editor
          butter.editor.editTrackEvent( trackEvent );

          if ( options.start ) {
            options.start();
          }
        },
        stop: function( event, ui ) {
          iframeCover.style.display = "none";

          calculateFinalPositions( event, ui, trackEvent, targetContainer, dragContainer, options );
        }
      });
    };

    /**
     * Member: resizable
     *
     * Makes a container resizable using jQueryUI
     *
     * @param {TrackEvent} trackEvent: The trackEvent to update when size changes
     * @param {DOMElement} resizeContainer: the container which to apply resizable to
     * @param {media} The current media's target element in Butter ( parent container )
     * @param {Object} extra options to apply to the resizeable call
     *                 Options are:
     *                    {String} handlePositions: describes where to position resize handles ( i.e. "n,s,e,w" )
     *                              - Recommended that this option is specified due to a bug in z-indexing with
     *                                jQueryUI Resizable.
     *                    {Function} start: Function to execute on resize start event
     *                    {Function} end: Fucntion to execute on resize end event
     *                    {Number} minWidth: Minimum width that the resizeContainer should be
     *                    {Number} minHeight: Minimum height that the resizeContainer should be
     */
    global.EditorHelper.resizable = function( trackEvent, resizeContainer, targetContainer, options ) {
      if ( $( resizeContainer ).data( "resizable" ) ) {
        return;
      }

      var iframeCover = targetContainer.querySelector( ".butter-iframe-fix" );

      options = options || {};

      $( resizeContainer ).resizable({
        handles: options.handlePositions,
        start: function() {
          iframeCover.style.display = "block";

          // Open the editor
          butter.editor.editTrackEvent( trackEvent );

          if ( options.start ) {
            options.start();
          }
        },
        containment: "parent",
        stop: function( event, ui ) {
          iframeCover.style.display = "none";

          calculateFinalPositions( event, ui, trackEvent, targetContainer, resizeContainer, options );
        }
      });
    };

    /**
     * Member: contentEditable
     *
     * Makes a container's content editable using contenteditable
     *
     * @param {TrackEvent} trackEvent: The trackEvent to update when content changes
     * @param {DOMElement} contentContainer: the container which to listen for changes and set as editable
     */
    global.EditorHelper.contentEditable = function( trackEvent, contentContainers ) {
      var newText = "",
          contentContainer,
          updateText,
          updateTrackEvent,
          onBlur,
          onKeyDown,
          onMouseDown;

      updateText = function() {
        newText = "";
        for ( var i = 0, l = contentContainers.length; i < l; i++ ) {
          contentContainer = contentContainers[ i ];
          contentContainer.innerHTML = contentContainer.innerHTML.replace( /<br>/g, "\n" );
          newText += contentContainer.textContent;
          if ( i < l - 1 ) {
            newText += "\n";
          }
        }
      };
      updateTrackEvent = function() {
        blurActiveEl();
        trackEvent.update({
          text: newText
        });
      };
      onBlur = function() {
        // store the new text.
        updateText();
        // update the text after any existing events are done.
        // this way we do not revert any other event's changes.
        setTimeout( updateTrackEvent, 0 );
      };
      onKeyDown = function( e ) {
        // enter key for an update.
        // shift + enter for newline.
        if ( !e.shiftKey && e.keyCode === 13 ) {
          updateText();
          updateTrackEvent();
        }
      };
      onMouseDown = function( e ) {
        e.stopPropagation();

        // Open the editor
        butter.editor.editTrackEvent( trackEvent );

        $( contentContainer ).draggable( "destroy" );
      };

      for ( var i = 0, l = contentContainers.length; i < l; i++ ) {
        contentContainer = contentContainers[ i ];
        if ( contentContainer ) {
          contentContainer.addEventListener( "blur", onBlur, false );
          contentContainer.addEventListener( "keydown", onKeyDown, false );
          contentContainer.addEventListener( "mousedown", onMouseDown, false );
          contentContainer.setAttribute( "contenteditable", "true" );
        }
      }
    };

    var mimeTypeMapping = {
      "image/gif": "image/png",
      "image/jpeg": "image/jpeg",
      "image/png": "image/png"
    };

    /**
     * Member: droppable
     *
     * Make a container listen for drop events for loading images from a local machine
     *
     * @param {TrackEvent} trackEvent: The trackEvent to update when content changes
     * @param {DOMElement} dropContainer: The container that listens for the drop events
     */

    global.EditorHelper.droppable = function( trackEvent, dropContainer ) {
      dropContainer.addEventListener( "dragover", function( e ) {
        e.preventDefault();
        dropContainer.classList.add( "butter-dragover" );
      }, false );

      dropContainer.addEventListener( "dragleave", function( e ) {
        e.preventDefault();
        dropContainer.classList.remove( "butter-dragover" );
      }, false );

      dropContainer.addEventListener( "mousedown", function( e ) {
        // Prevent being able to drag the images inside and re drop them
        e.preventDefault();
      }, false );

      dropContainer.addEventListener( "drop", function( e ) {
        var file, imgSrc, imgURI, image, div;

        e.preventDefault();
        e.stopPropagation();

        dropContainer.classList.remove( "butter-dragover" );

        if ( !e.dataTransfer || !e.dataTransfer.files || !e.dataTransfer.files[ 0 ] ) {
          butter.dispatch( "droppable-unsupported" );
          return;
        }

        file = e.dataTransfer.files[ 0 ];

        // If we can't reasonably convert whatever they're dropping on us then bail
        var exportAs = mimeTypeMapping[ file.type ];
        if ( !exportAs ) {
          butter.dispatch( "filetype-unsupported" );
          return;
        }

        if ( window.URL && window.URL.createObjectURL ) {
          imgSrc = window.URL.createObjectURL( file );
        } else if ( window.webkitURL && window.webkitURL.createObjectURL ) {
          imgSrc = window.webkitURL.createObjectURL( file );
        } else {
          butter.dispatch( "droppable-unsupported" );
        }

        // So yeah, Opera has the functionality namespaced but all it currently does is:
        // window.URL.createObjectURL = function(obj) {
        //   return obj;
        // };
        if ( imgSrc === file ) {
          butter.dispatch( "droppable-unsupported" );
          return;
        } else {

          image = document.createElement( "img" );
          image.onload = function() {
            var width = this.width,
                height = this.height,
                wRatio, hRatio, resizeRatio,
                scaledWidth, scaledHeight,
                canvas = document.createElement( "canvas" ),
                context;

            // Fit image nicely into our largest embed size, using
            // the longest side and aspect ratio. Inspired by:
            // http://stackoverflow.com/questions/7863653/algorithm-to-resize-image-and-maintain-aspect-ratio-to-fit-iphone
            if ( width >= height ) {
              if ( width <= MAX_IMAGE_WIDTH && height <= MAX_IMAGE_HEIGHT ) {
                scaledWidth = width;
                scaledHeight = height;
              } else {
                wRatio = MAX_IMAGE_WIDTH / width;
                hRatio = MAX_IMAGE_HEIGHT / height;
                resizeRatio = Math.min( wRatio, hRatio );
                scaledHeight = height * resizeRatio;
                scaledWidth = width * resizeRatio;
              }
            } else {
              if ( height <= MAX_IMAGE_WIDTH && width <= MAX_IMAGE_HEIGHT ) {
                scaledWidth = width;
                scaledHeight = height;
              } else {
                wRatio = MAX_IMAGE_HEIGHT / width;
                hRatio = MAX_IMAGE_WIDTH / height;
                resizeRatio = Math.min( wRatio, hRatio );
                scaledHeight = height * resizeRatio;
                scaledWidth = width * resizeRatio;
              }
            }

            canvas.width = scaledWidth;
            canvas.height = scaledHeight;
            context = canvas.getContext( "2d" );
            context.drawImage( this, 0, 0, scaledWidth, scaledHeight );

            imgURI = canvas.toDataURL( exportAs );

            // If the browser's canvas impl. doesn't support whatever we requested then bail
            if ( imgURI.indexOf( exportAs ) === -1 ) {
              butter.dispatch( "droppable-unsupported" );
              return;
            }

            trackEvent.update( { src: imgURI } );

            if ( window.URL && window.URL.revokeObjectURL ) {
              window.URL.revokeObjectURL( imgSrc );
            } else if ( window.webkitURL && window.webkitURL.revokeObjectURL ) {
              window.webkitURL.revokeObjectURL( imgSrc );
            }
          };
          image.src = imgSrc;

          butter.editor.editTrackEvent( trackEvent );

          // Force image to download, esp. Opera. We can't use
          // "display: none", since that makes it invisible, and
          // thus not load.  Opera also requires the image be
          // in the DOM before it will load.
          div = document.createElement( "div" );
          div.setAttribute( "data-butter-exclude", "true" );
          div.className = "butter-image-preload";
          div.appendChild( image );
          document.body.appendChild( div );
        }
      }, false );
    };

    function _updateFunction( e ) {

      var trackEvent = e.target;

      if ( trackEvent.popcornTrackEvent && plugins[ trackEvent.type ] ) {
        plugins[ trackEvent.type ]( trackEvent, butter.currentMedia.popcorn.popcorn );
      }
    } //updateFunction

    butter.listen( "trackeventupdated", _updateFunction );
  };

  EditorHelper.addPlugin = function( plugin, callback ) {
    plugins[ plugin ] = callback;
  };

  global.EditorHelper = EditorHelper;

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


<commit_before>/*    Copyright 2020 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename,"debug");

const rclient = require('../util/redis_manager.js').getRedisClient();
const pclient = require('../util/redis_manager.js').getPublishClient();
const eapi = require('./EventApi.js');

const KEY_EVENT_REQUEST_STATE = "event:request:state";
const KEY_EVENT_REQUEST_ACTION = "event:request:action";
const KEY_EVENT_REQUEST_CLEAN = "event:request:clean";
const STATE_REQUIRED_FIELDS = [ "ts", "state_type", "state_key", "state_value"]
const ACTION_REQUIRED_FIELDS = [ "ts", "action_type", "action_value"]

/*
 * EventRequestApi send event requests onto redis channels for processing
 *
 * Supported APIs:
 * - add event : STATE of ACTION
 * - clean event
 * - list events
 *
 */
class EventRequestApi {
    constructor() {
    }

    isNumber(x) {
        return ( typeof x === 'number' && ! isNaN(x));
    }

    async addStateEvent(state_type,state_key,state_value,labels=null, ts=Date.now()) {
        log.info("add state event");

        try {
            let event_obj = {
                "ts": ts,
                "event_type": "state",
                "state_type": state_type,
                "state_key": state_key,
                "state_value": state_value
            }

            if ( labels !== null ) {
                event_obj["labels"] = labels;
            }

            if ( ! this.isNumber(state_value) ) {
                throw new Error(`state_value(${state_value}) is NOT a number`);
            }
            const event_json = JSON.stringify(event_obj);
            await pclient.publishAsync(KEY_EVENT_REQUEST_STATE,event_json);
        } catch (err) {
            log.error(`failed to publish state event(${event_json}), ${err}`);
        }
    }

    async addActionEvent(action_type,action_value,labels=null, ts=Date.now()) {
        log.info("add action event");

        try {
            let event_obj = {
                "ts": ts,
                "event_type": "action",
                "action_type": action_type,
                "action_value": action_value
            }
            if ( labels !== null ) {
                event_obj["labels"] = labels;
            }

            if ( ! this.isNumber(action_value) ) {
                throw new Error(`action_value(${action_value}) is NOT a number`);
            }
            const event_json = JSON.stringify(event_obj);
            await pclient.publishAsync(KEY_EVENT_REQUEST_ACTION,event_json);
        } catch (err) {
            log.error(`failed to publish action event(${event_json}), ${err}`);
        }
    }

    async cleanEvents(begin=0, end=0) {
        log.info("clean events");

        try {
            let clean_obj = {
                "begin": begin,
                "end" : end
            }
            if ( ! this.isNumber(begin) || ! this.isNumber(end)) {
                throw new Error(`begin(${begin}) or end(${end}) is NOT number`);
            }
            const clean_json = JSON.stringify(clean_obj);
            await pclient.publishAsync(KEY_EVENT_REQUEST_CLEAN,clean_json);
        } catch (err) {
            log.error(`failed to clean events(${event_json}), ${err}`);
        }
    }

    async listEvents(begin=0, end=0, limit_offset, limit_count) {
        log.info("list events");

        try {
            let result = await eapi.listEvents(begin,end,limit_offset,limit_count);
            return result;
        } catch (err) {
            log.error(`failed to list events(${begin},${end}), ${err}`);
        }
    }
}

( async () => {
    let x = new EventRequestApi();
    console.log(await x.listEvents(0,Date.now()));
})();

module.exports = new EventRequestApi();<commit_msg>remove test code<commit_after>/*    Copyright 2020 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename,"debug");

const rclient = require('../util/redis_manager.js').getRedisClient();
const pclient = require('../util/redis_manager.js').getPublishClient();
const eapi = require('./EventApi.js');

const KEY_EVENT_REQUEST_STATE = "event:request:state";
const KEY_EVENT_REQUEST_ACTION = "event:request:action";
const KEY_EVENT_REQUEST_CLEAN = "event:request:clean";
const STATE_REQUIRED_FIELDS = [ "ts", "state_type", "state_key", "state_value"]
const ACTION_REQUIRED_FIELDS = [ "ts", "action_type", "action_value"]

/*
 * EventRequestApi send event requests onto redis channels for processing
 *
 * Supported APIs:
 * - add event : STATE of ACTION
 * - clean event
 * - list events
 *
 */
class EventRequestApi {
    constructor() {
    }

    isNumber(x) {
        return ( typeof x === 'number' && ! isNaN(x));
    }

    async addStateEvent(state_type,state_key,state_value,labels=null, ts=Date.now()) {
        log.info("add state event");

        try {
            let event_obj = {
                "ts": ts,
                "event_type": "state",
                "state_type": state_type,
                "state_key": state_key,
                "state_value": state_value
            }

            if ( labels !== null ) {
                event_obj["labels"] = labels;
            }

            if ( ! this.isNumber(state_value) ) {
                throw new Error(`state_value(${state_value}) is NOT a number`);
            }
            const event_json = JSON.stringify(event_obj);
            await pclient.publishAsync(KEY_EVENT_REQUEST_STATE,event_json);
        } catch (err) {
            log.error(`failed to publish state event(${event_json}), ${err}`);
        }
    }

    async addActionEvent(action_type,action_value,labels=null, ts=Date.now()) {
        log.info("add action event");

        try {
            let event_obj = {
                "ts": ts,
                "event_type": "action",
                "action_type": action_type,
                "action_value": action_value
            }
            if ( labels !== null ) {
                event_obj["labels"] = labels;
            }

            if ( ! this.isNumber(action_value) ) {
                throw new Error(`action_value(${action_value}) is NOT a number`);
            }
            const event_json = JSON.stringify(event_obj);
            await pclient.publishAsync(KEY_EVENT_REQUEST_ACTION,event_json);
        } catch (err) {
            log.error(`failed to publish action event(${event_json}), ${err}`);
        }
    }

    async cleanEvents(begin=0, end=0) {
        log.info("clean events");

        try {
            let clean_obj = {
                "begin": begin,
                "end" : end
            }
            if ( ! this.isNumber(begin) || ! this.isNumber(end)) {
                throw new Error(`begin(${begin}) or end(${end}) is NOT number`);
            }
            const clean_json = JSON.stringify(clean_obj);
            await pclient.publishAsync(KEY_EVENT_REQUEST_CLEAN,clean_json);
        } catch (err) {
            log.error(`failed to clean events(${event_json}), ${err}`);
        }
    }

    async listEvents(begin=0, end=0, limit_offset, limit_count) {
        log.info("list events");

        try {
            let result = await eapi.listEvents(begin,end,limit_offset,limit_count);
            return result;
        } catch (err) {
            log.error(`failed to list events(${begin},${end}), ${err}`);
        }
    }
}

/*
( async () => {
    let x = new EventRequestApi();
    console.log(await x.listEvents(0,Date.now()));
})();
*/

module.exports = new EventRequestApi();<|endoftext|>


<commit_before>app.constant("SIGNOUT_ENDPOINT", "php/apis/singout/");
app.service("signoutService", function($http, SIGNOUT_ENDPOINT) {

	this.signout = function() {
		return($http.get(this.SIGNOUT_ENDPOINT));
	};
});<commit_msg>replaced signout instaed of singout https://app.asana.com/0/161555263951762/170520114054348<commit_after>app.constant("SIGNOUT_ENDPOINT", "php/apis/signout/");
app.service("signoutService", function($http, SIGNOUT_ENDPOINT) {

	this.signout = function() {
		return($http.get(this.SIGNOUT_ENDPOINT));
	};
});<|endoftext|>


<commit_before>require (['dojo/domReady!'], function () {

});<commit_msg>Refresh button state (disabled) is updated based on active tasks.<commit_after>require ([
    'dojo/query',
    'dojo/dom-class',
    'dojo/topic',
    'dojo/on',

	'dojo/domReady!'
], function (
	query,
	domClass,
	topic,
	on
) {

	var refreshButtons = query ('.js-datasource-refresh'),
		currentHarvesting = false;

	topic.subscribe ('publisher/active-tasks', function (tasks) {
		var isHarvesting = false;

		for (var i = 0; i < tasks.list.length; ++ i) {
			var task = tasks.list[i];
			if (task.message.type == 'HARVEST') {
				isHarvesting = true;
				break;
			}
		}

		if (isHarvesting === currentHarvesting) {
			return;
		}

		currentHarvesting = isHarvesting;

		refreshButtons.forEach (function (button) {
			domClass[isHarvesting ? 'add' : 'remove'] (button, 'disabled');
		});
	});

	refreshButtons.on ('click', function (e) {
		e.preventDefault ();
		e.stopPropagation ();

		if (domClass.contains (this, 'disabled')) {
			return;
		}

		domClass.add (this, 'disabled');
	});
});<|endoftext|>