<commit_before>/**
 * `core/widgets` data store: widgets info.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * External dependencies
 */
import invariant from 'invariant';
import intersection from 'lodash/intersection';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import { isInactiveWidgetState, normalizeWidgetModules } from '../util';
import { CORE_WIDGETS, WIDGET_WIDTHS } from './constants';
import { createReducer } from '../../../../js/googlesitekit/data/create-reducer';

const { createRegistrySelector } = Data;

const ASSIGN_WIDGET = 'ASSIGN_WIDGET';
const REGISTER_WIDGET = 'REGISTER_WIDGET';
const SET_WIDGET_STATE = 'SET_WIDGET_STATE';
const UNSET_WIDGET_STATE = 'UNSET_WIDGET_STATE';

const WidgetWidthKeys = Object.keys( WIDGET_WIDTHS )
	.map( ( key ) => `WIDGET_WIDTHS.${ key }` )
	.join( ', ' );

export const initialState = {
	areaAssignments: {},
	registryKey: undefined,
	widgets: {},
	widgetStates: {},
};

export const actions = {
	/**
	 * Assigns an existing widget (by slug) to a widget area(s).
	 *
	 * @since 1.9.0
	 *
	 * @param {string}         slug      Widget slug.
	 * @param {(string|Array)} areaSlugs Widget Area slug(s).
	 * @return {Object} Redux-style action.
	 */
	assignWidget( slug, areaSlugs ) {
		return {
			payload: {
				slug,
				areaSlugs:
					typeof areaSlugs === 'string' ? [ areaSlugs ] : areaSlugs,
			},
			type: ASSIGN_WIDGET,
		};
	},

	/**
	 * Registers a widget with a given slug and settings.
	 *
	 * @since 1.9.0
	 * @since 1.12.0 Added wrapWidget setting.
	 *
	 * @param {string}                slug                  Widget's slug.
	 * @param {Object}                settings              Widget's settings.
	 * @param {WPComponent}           settings.Component    React component used to display the contents of this widget.
	 * @param {number}                [settings.priority]   Optional. Widget's priority for ordering (lower number is higher priority, like WordPress hooks). Default is: 10.
	 * @param {string|Array.<string>} [settings.width]      Optional. Widget's maximum width to occupy. Default is: "quarter". One of: "quarter", "half", "full".
	 * @param {boolean}               [settings.wrapWidget] Optional. Whether to wrap the component with the <Widget> wrapper. Default is: true.
	 * @param {string|Array.<string>} [settings.modules]    Optional. Widget's associated modules.
	 * @return {Object} Redux-style action.
	 */
	registerWidget(
		slug,
		{
			Component,
			priority = 10,
			width = WIDGET_WIDTHS.QUARTER,
			wrapWidget = true,
			modules,
		} = {}
	) {
		const allWidths = Object.values( WIDGET_WIDTHS );

		invariant( Component, 'component is required to register a widget.' );
		invariant(
			( Array.isArray( width ) &&
				width.some( allWidths.includes, allWidths ) ) ||
				( ! Array.isArray( width ) && allWidths.includes( width ) ),
			`Widget width should be one of: ${ WidgetWidthKeys }, but "${ width }" was provided.`
		);

		return {
			payload: {
				slug,
				settings: {
					Component,
					priority,
					width,
					wrapWidget,
					modules: normalizeWidgetModules( modules ),
				},
			},
			type: REGISTER_WIDGET,
		};
	},

	/**
	 * Sets widget state for a given widget.
	 *
	 * Used internally by various components that can be returned by
	 * registered widget components.
	 *
	 * @since 1.25.0
	 * @private
	 *
	 * @param {string}      slug       Widget slug.
	 * @param {WPComponent} Component  Component returned by the widget.
	 * @param {Object}      [metadata] Relevant metadata / props passed to
	 *                                 the Component instance.
	 * @return {Object} Redux-style action.
	 */
	setWidgetState( slug, Component, metadata = {} ) {
		return {
			payload: {
				slug,
				Component,
				metadata,
			},
			type: SET_WIDGET_STATE,
		};
	},

	/**
	 * Unsets widget state for a given widget.
	 *
	 * The widget state will only be unset if the current widget state matches
	 * exactly the passed parameters.
	 *
	 * Used internally by various components that can be returned by
	 * registered widget components.
	 *
	 * @since 1.25.0
	 * @private
	 *
	 * @param {string}      slug       Widget slug.
	 * @param {WPComponent} Component  Component returned by the widget.
	 * @param {Object}      [metadata] Relevant metadata / props passed to
	 *                                 the Component instance.
	 * @return {Object} Redux-style action.
	 */
	unsetWidgetState( slug, Component, metadata = {} ) {
		return {
			payload: {
				slug,
				Component,
				metadata,
			},
			type: UNSET_WIDGET_STATE,
		};
	},
};

export const controls = {};

export const reducer = createReducer( ( draft, { type, payload } ) => {
	switch ( type ) {
		case ASSIGN_WIDGET: {
			const { slug, areaSlugs } = payload;

			areaSlugs.forEach( ( areaSlug ) => {
				if ( draft.areaAssignments[ areaSlug ] === undefined ) {
					draft.areaAssignments[ areaSlug ] = [];
				}

				if ( ! draft.areaAssignments[ areaSlug ].includes( slug ) ) {
					draft.areaAssignments[ areaSlug ].push( slug );
				}
			} );

			return draft;
		}

		case REGISTER_WIDGET: {
			const { slug, settings } = payload;

			if ( draft.widgets[ slug ] !== undefined ) {
				global.console.warn(
					`Could not register widget with slug "${ slug }". Widget "${ slug }" is already registered.`
				);

				return draft;
			}

			draft.widgets[ slug ] = { ...settings, slug };
			return draft;
		}

		case SET_WIDGET_STATE: {
			const { slug, Component, metadata } = payload;

			draft.widgetStates[ slug ] = { Component, metadata };
			return draft;
		}

		case UNSET_WIDGET_STATE: {
			const { slug, Component, metadata } = payload;

			if (
				draft.widgetStates?.[ slug ]?.Component === Component &&
				draft.widgetStates?.[ slug ]?.metadata === metadata
			) {
				return draft;
			}

			return draft;
		}

		default: {
			return draft;
		}
	}
} );

export const resolvers = {};

export const selectors = {
	/**
	 * Checks if a widget with a given slug is active.
	 *
	 * Returns `true` if the widget area is active.
	 * Returns `false` if the widget area is NOT active.
	 *
	 * @since 1.47.0
	 *
	 * @param {Object} state Data store's state.
	 * @param {string} slug  Widget's slug.
	 * @return {boolean} `true`/`false` based on whether widget is active.
	 */
	isWidgetActive: createRegistrySelector( ( select ) => ( state, slug ) => {
		invariant( slug, 'slug is required to check a widget is active.' );

		return ! isInactiveWidgetState(
			select( CORE_WIDGETS ).getWidgetState( slug )
		);
	} ),

	/**
	 * Checks if a widget has been registered with a given slug.
	 *
	 * Returns `true` if the widget area has been registered.
	 * Returns `false` if the widget area has NOT been registered.
	 *
	 * @since 1.9.0
	 *
	 * @param {Object} state Data store's state.
	 * @param {string} slug  Widget's slug.
	 * @return {boolean} `true`/`false` based on whether widget has been registered.
	 */
	isWidgetRegistered( state, slug ) {
		const { widgets } = state;

		return widgets[ slug ] !== undefined;
	},

	/**
	 * Returns all widgets registered for a given widget area.
	 *
	 * Returns an array of all widgets for a given area.
	 * The widgets are returned in order of their priority, so can be rendered in
	 * the order provided by the selector.
	 *
	 * @since 1.9.0
	 *
	 * @param {Object}                state             Data store's state.
	 * @param {string}                widgetAreaSlug    Widget context to get areas for.
	 * @param {Object}                options           Widgets selection options.
	 * @param {string|Array.<string>} [options.modules] Optional. Widget's associated modules.
	 * @return {Array} An ordered array of widgets for this area.
	 */
	getWidgets( state, widgetAreaSlug, { modules } = {} ) {
		invariant( widgetAreaSlug, 'widgetAreaSlug is required.' );

		const { areaAssignments } = state;

		let widgets = Object.values( state.widgets ).filter( ( widget ) =>
			areaAssignments[ widgetAreaSlug ]?.includes( widget.slug )
		);

		if ( modules ) {
			const allowedModules = normalizeWidgetModules( modules );
			widgets = widgets.filter( ( widget ) => {
				if ( ! widget.modules?.length ) {
					return true;
				}

				return (
					intersection( widget.modules, allowedModules ).length ===
					widget.modules.length
				);
			} );
		}

		return widgets.sort( ( a, b ) => a.priority - b.priority );
	},

	/**
	 * Returns a single widget, by slug.
	 *
	 * @since 1.11.0
	 *
	 * @param {Object} state Data store's state.
	 * @param {string} slug  Widget slug.
	 * @return {Object|null} A widget object, if one exists.
	 */
	getWidget( state, slug ) {
		invariant( slug, 'slug is required to get a widget.' );

		return state.widgets[ slug ] || null;
	},

	/**
	 * Returns the state data of a widget by its slug.
	 *
	 * Used internally by the WidgetAreaRenderer component to recognize
	 * widgets in special states.
	 *
	 * @since 1.25.0
	 * @private
	 *
	 * @param {Object} state Data store's state.
	 * @param {string} slug  Widget slug.
	 * @return {Object|null} Object with `Component` and `metadata` properties,
	 *                       if the widget has a special state, or `null`.
	 */
	getWidgetState( state, slug ) {
		return state.widgetStates[ slug ] || null;
	},

	/**
	 * Returns all widget states.
	 *
	 * @since 1.28.0
	 * @private
	 *
	 * @param {Object} state Data store's state.
	 * @return {Object} Widget states, keyed by slug, with a value containing the component and metadata.
	 */
	getWidgetStates( state ) {
		return state.widgetStates;
	},
};

export default {
	initialState,
	actions,
	controls,
	reducer,
	resolvers,
	selectors,
};
<commit_msg>Fix tests.<commit_after>/**
 * `core/widgets` data store: widgets info.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * External dependencies
 */
import invariant from 'invariant';
import intersection from 'lodash/intersection';
import { original } from 'immer';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import { isInactiveWidgetState, normalizeWidgetModules } from '../util';
import { CORE_WIDGETS, WIDGET_WIDTHS } from './constants';
import { createReducer } from '../../../../js/googlesitekit/data/create-reducer';

const { createRegistrySelector } = Data;

const ASSIGN_WIDGET = 'ASSIGN_WIDGET';
const REGISTER_WIDGET = 'REGISTER_WIDGET';
const SET_WIDGET_STATE = 'SET_WIDGET_STATE';
const UNSET_WIDGET_STATE = 'UNSET_WIDGET_STATE';

const WidgetWidthKeys = Object.keys( WIDGET_WIDTHS )
	.map( ( key ) => `WIDGET_WIDTHS.${ key }` )
	.join( ', ' );

export const initialState = {
	areaAssignments: {},
	registryKey: undefined,
	widgets: {},
	widgetStates: {},
};

export const actions = {
	/**
	 * Assigns an existing widget (by slug) to a widget area(s).
	 *
	 * @since 1.9.0
	 *
	 * @param {string}         slug      Widget slug.
	 * @param {(string|Array)} areaSlugs Widget Area slug(s).
	 * @return {Object} Redux-style action.
	 */
	assignWidget( slug, areaSlugs ) {
		return {
			payload: {
				slug,
				areaSlugs:
					typeof areaSlugs === 'string' ? [ areaSlugs ] : areaSlugs,
			},
			type: ASSIGN_WIDGET,
		};
	},

	/**
	 * Registers a widget with a given slug and settings.
	 *
	 * @since 1.9.0
	 * @since 1.12.0 Added wrapWidget setting.
	 *
	 * @param {string}                slug                  Widget's slug.
	 * @param {Object}                settings              Widget's settings.
	 * @param {WPComponent}           settings.Component    React component used to display the contents of this widget.
	 * @param {number}                [settings.priority]   Optional. Widget's priority for ordering (lower number is higher priority, like WordPress hooks). Default is: 10.
	 * @param {string|Array.<string>} [settings.width]      Optional. Widget's maximum width to occupy. Default is: "quarter". One of: "quarter", "half", "full".
	 * @param {boolean}               [settings.wrapWidget] Optional. Whether to wrap the component with the <Widget> wrapper. Default is: true.
	 * @param {string|Array.<string>} [settings.modules]    Optional. Widget's associated modules.
	 * @return {Object} Redux-style action.
	 */
	registerWidget(
		slug,
		{
			Component,
			priority = 10,
			width = WIDGET_WIDTHS.QUARTER,
			wrapWidget = true,
			modules,
		} = {}
	) {
		const allWidths = Object.values( WIDGET_WIDTHS );

		invariant( Component, 'component is required to register a widget.' );
		invariant(
			( Array.isArray( width ) &&
				width.some( allWidths.includes, allWidths ) ) ||
				( ! Array.isArray( width ) && allWidths.includes( width ) ),
			`Widget width should be one of: ${ WidgetWidthKeys }, but "${ width }" was provided.`
		);

		return {
			payload: {
				slug,
				settings: {
					Component,
					priority,
					width,
					wrapWidget,
					modules: normalizeWidgetModules( modules ),
				},
			},
			type: REGISTER_WIDGET,
		};
	},

	/**
	 * Sets widget state for a given widget.
	 *
	 * Used internally by various components that can be returned by
	 * registered widget components.
	 *
	 * @since 1.25.0
	 * @private
	 *
	 * @param {string}      slug       Widget slug.
	 * @param {WPComponent} Component  Component returned by the widget.
	 * @param {Object}      [metadata] Relevant metadata / props passed to
	 *                                 the Component instance.
	 * @return {Object} Redux-style action.
	 */
	setWidgetState( slug, Component, metadata = {} ) {
		return {
			payload: {
				slug,
				Component,
				metadata,
			},
			type: SET_WIDGET_STATE,
		};
	},

	/**
	 * Unsets widget state for a given widget.
	 *
	 * The widget state will only be unset if the current widget state matches
	 * exactly the passed parameters.
	 *
	 * Used internally by various components that can be returned by
	 * registered widget components.
	 *
	 * @since 1.25.0
	 * @private
	 *
	 * @param {string}      slug       Widget slug.
	 * @param {WPComponent} Component  Component returned by the widget.
	 * @param {Object}      [metadata] Relevant metadata / props passed to
	 *                                 the Component instance.
	 * @return {Object} Redux-style action.
	 */
	unsetWidgetState( slug, Component, metadata = {} ) {
		return {
			payload: {
				slug,
				Component,
				metadata,
			},
			type: UNSET_WIDGET_STATE,
		};
	},
};

export const controls = {};

export const reducer = createReducer( ( draft, { type, payload } ) => {
	switch ( type ) {
		case ASSIGN_WIDGET: {
			const { slug, areaSlugs } = payload;

			areaSlugs.forEach( ( areaSlug ) => {
				if ( draft.areaAssignments[ areaSlug ] === undefined ) {
					draft.areaAssignments[ areaSlug ] = [];
				}

				if ( ! draft.areaAssignments[ areaSlug ].includes( slug ) ) {
					draft.areaAssignments[ areaSlug ].push( slug );
				}
			} );

			return draft;
		}

		case REGISTER_WIDGET: {
			const { slug, settings } = payload;

			if ( draft.widgets[ slug ] !== undefined ) {
				global.console.warn(
					`Could not register widget with slug "${ slug }". Widget "${ slug }" is already registered.`
				);

				return draft;
			}

			draft.widgets[ slug ] = { ...settings, slug };
			return draft;
		}

		case SET_WIDGET_STATE: {
			const { slug, Component, metadata } = payload;

			draft.widgetStates[ slug ] = { Component, metadata };
			return draft;
		}

		case UNSET_WIDGET_STATE: {
			const { slug, Component, metadata } = payload;

			if (
				draft.widgetStates?.[ slug ]?.Component === Component &&
				original( draft.widgetStates?.[ slug ]?.metadata ) === metadata
			) {
				delete draft.widgetStates[ slug ];
			}

			return draft;
		}

		default: {
			return draft;
		}
	}
} );

export const resolvers = {};

export const selectors = {
	/**
	 * Checks if a widget with a given slug is active.
	 *
	 * Returns `true` if the widget area is active.
	 * Returns `false` if the widget area is NOT active.
	 *
	 * @since 1.47.0
	 *
	 * @param {Object} state Data store's state.
	 * @param {string} slug  Widget's slug.
	 * @return {boolean} `true`/`false` based on whether widget is active.
	 */
	isWidgetActive: createRegistrySelector( ( select ) => ( state, slug ) => {
		invariant( slug, 'slug is required to check a widget is active.' );

		return ! isInactiveWidgetState(
			select( CORE_WIDGETS ).getWidgetState( slug )
		);
	} ),

	/**
	 * Checks if a widget has been registered with a given slug.
	 *
	 * Returns `true` if the widget area has been registered.
	 * Returns `false` if the widget area has NOT been registered.
	 *
	 * @since 1.9.0
	 *
	 * @param {Object} state Data store's state.
	 * @param {string} slug  Widget's slug.
	 * @return {boolean} `true`/`false` based on whether widget has been registered.
	 */
	isWidgetRegistered( state, slug ) {
		const { widgets } = state;

		return widgets[ slug ] !== undefined;
	},

	/**
	 * Returns all widgets registered for a given widget area.
	 *
	 * Returns an array of all widgets for a given area.
	 * The widgets are returned in order of their priority, so can be rendered in
	 * the order provided by the selector.
	 *
	 * @since 1.9.0
	 *
	 * @param {Object}                state             Data store's state.
	 * @param {string}                widgetAreaSlug    Widget context to get areas for.
	 * @param {Object}                options           Widgets selection options.
	 * @param {string|Array.<string>} [options.modules] Optional. Widget's associated modules.
	 * @return {Array} An ordered array of widgets for this area.
	 */
	getWidgets( state, widgetAreaSlug, { modules } = {} ) {
		invariant( widgetAreaSlug, 'widgetAreaSlug is required.' );

		const { areaAssignments } = state;

		let widgets = Object.values( state.widgets ).filter( ( widget ) =>
			areaAssignments[ widgetAreaSlug ]?.includes( widget.slug )
		);

		if ( modules ) {
			const allowedModules = normalizeWidgetModules( modules );
			widgets = widgets.filter( ( widget ) => {
				if ( ! widget.modules?.length ) {
					return true;
				}

				return (
					intersection( widget.modules, allowedModules ).length ===
					widget.modules.length
				);
			} );
		}

		return widgets.sort( ( a, b ) => a.priority - b.priority );
	},

	/**
	 * Returns a single widget, by slug.
	 *
	 * @since 1.11.0
	 *
	 * @param {Object} state Data store's state.
	 * @param {string} slug  Widget slug.
	 * @return {Object|null} A widget object, if one exists.
	 */
	getWidget( state, slug ) {
		invariant( slug, 'slug is required to get a widget.' );

		return state.widgets[ slug ] || null;
	},

	/**
	 * Returns the state data of a widget by its slug.
	 *
	 * Used internally by the WidgetAreaRenderer component to recognize
	 * widgets in special states.
	 *
	 * @since 1.25.0
	 * @private
	 *
	 * @param {Object} state Data store's state.
	 * @param {string} slug  Widget slug.
	 * @return {Object|null} Object with `Component` and `metadata` properties,
	 *                       if the widget has a special state, or `null`.
	 */
	getWidgetState( state, slug ) {
		return state.widgetStates[ slug ] || null;
	},

	/**
	 * Returns all widget states.
	 *
	 * @since 1.28.0
	 * @private
	 *
	 * @param {Object} state Data store's state.
	 * @return {Object} Widget states, keyed by slug, with a value containing the component and metadata.
	 */
	getWidgetStates( state ) {
		return state.widgetStates;
	},
};

export default {
	initialState,
	actions,
	controls,
	reducer,
	resolvers,
	selectors,
};
<|endoftext|>


<commit_before>/**
 * Analytics Account Select component.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * WordPress dependencies
 */
import { useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import ProgressBar from '../../../components/progress-bar';
import { Select, Option } from '../../../material-components';
import { STORE_NAME, ACCOUNT_CREATE } from '../datastore/constants';
import { trackEvent } from '../../../util';
const { useSelect, useDispatch } = Data;

export default function AccountSelect() {
	const accounts = useSelect( ( select ) => select( STORE_NAME ).getAccounts() );
	const accountID = useSelect( ( select ) => select( STORE_NAME ).getAccountID() );
	const hasExistingTag = useSelect( ( select ) => select( STORE_NAME ).hasExistingTag() );
	const isFetchingAccounts = useSelect( ( select ) => select( STORE_NAME ).isFetchingAccounts() );
	const isLoading = accounts === undefined || isFetchingAccounts;

	const { selectAccount } = useDispatch( STORE_NAME );
	const onChange = useCallback( ( index, item ) => {
		const newAccountID = item.dataset.value;
		if ( accountID !== newAccountID ) {
			selectAccount( newAccountID );
			trackEvent( 'analytics_setup', 'account_change', newAccountID );
		}
	}, [ accountID ] );

	if ( isLoading ) {
		return <ProgressBar small />;
	}

	return (
		<Select
			className="googlesitekit-analytics__select-account"
			label={ __( 'Account', 'google-site-kit' ) }
			value={ accountID }
			onEnhancedChange={ onChange }
			disabled={ hasExistingTag }
			enhanced
			outlined
		>
			{ ( accounts || [] )
				.concat( {
					id: ACCOUNT_CREATE,
					name: __( 'Set up a new account', 'google-site-kit' ),
				} )
				.map( ( { id, name }, index ) => (
					<Option
						key={ index }
						value={ id }
					>
						{ name }
					</Option>
				) ) }
		</Select>
	);
}
<commit_msg>Assume accounts will always be an array.<commit_after>/**
 * Analytics Account Select component.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * WordPress dependencies
 */
import { useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import ProgressBar from '../../../components/progress-bar';
import { Select, Option } from '../../../material-components';
import { STORE_NAME, ACCOUNT_CREATE } from '../datastore/constants';
import { trackEvent } from '../../../util';
const { useSelect, useDispatch } = Data;

export default function AccountSelect() {
	const accounts = useSelect( ( select ) => select( STORE_NAME ).getAccounts() );
	const accountID = useSelect( ( select ) => select( STORE_NAME ).getAccountID() );
	const hasExistingTag = useSelect( ( select ) => select( STORE_NAME ).hasExistingTag() );
	const isFetchingAccounts = useSelect( ( select ) => select( STORE_NAME ).isFetchingAccounts() );
	const isLoading = accounts === undefined || isFetchingAccounts;

	const { selectAccount } = useDispatch( STORE_NAME );
	const onChange = useCallback( ( index, item ) => {
		const newAccountID = item.dataset.value;
		if ( accountID !== newAccountID ) {
			selectAccount( newAccountID );
			trackEvent( 'analytics_setup', 'account_change', newAccountID );
		}
	}, [ accountID ] );

	if ( isLoading ) {
		return <ProgressBar small />;
	}

	return (
		<Select
			className="googlesitekit-analytics__select-account"
			label={ __( 'Account', 'google-site-kit' ) }
			value={ accountID }
			onEnhancedChange={ onChange }
			disabled={ hasExistingTag }
			enhanced
			outlined
		>
			{ accounts
				.concat( {
					id: ACCOUNT_CREATE,
					name: __( 'Set up a new account', 'google-site-kit' ),
				} )
				.map( ( { id, name }, index ) => (
					<Option
						key={ index }
						value={ id }
					>
						{ name }
					</Option>
				) ) }
		</Select>
	);
}
<|endoftext|>


<commit_before>var crypto = {}

crypto.randomInteger = function (min, max) {
    var rand = min - 0.5 + Math.random() * (max - min + 1)
    rand = Math.round(rand);
    return rand;
}

crypto.decrypt = function (sock, raw, offset, size) {
    try {

        var temp = 0;
        for (var i = 0; i < size; i++) {
            var temp2 = raw[offset + i] & 0xFF;
            if (i == 8) {
                console.log(temp2);
            }
            raw[offset + i] = (temp2 ^ sock.client.newXorKeyDec[i & 15] ^ temp) & 0xff;
            if (i == 8) {
                console.log((raw[offset + i]).toString(16));
            }
            temp = temp2;
        }

        var old = (sock.client.newXorKeyDec[8] << 0x00) & 0xff;
        old |= (sock.client.newXorKeyDec[9] << 0x08) & 0xff00;
        old |= (sock.client.newXorKeyDec[10] << 0x10) & 0xff0000;
        old |= (sock.client.newXorKeyDec[11] << 0x18) & 0xff000000;

        old += size;

        sock.client.newXorKeyDec[8] = ((old >> 0) & 0xff);
        sock.client.newXorKeyDec[9] = ((old >> 0x08) & 0xff);
        sock.client.newXorKeyDec[10] = ((old >> 0x10) & 0xff);
        sock.client.newXorKeyDec[11] = ((old >> 0x18) & 0xff);

    } catch (ex) {
        console.log(ex);
    }
}

crypto.encrypt = function (sock, raw, offset, size) {
    try {

        var temp = 0;
        for (var i = 0; i < size; i++) {
            var temp2 = raw[offset + i] & 0xFF;
            temp = (temp2 ^ sock.client.newXorKeyEnc[i & 15] ^ temp) & 0xff;
            raw[offset + i] = temp;
        }

        var old = (sock.client.newXorKeyEnc[8] << 0x00) & 0xff;
        old |= (sock.client.newXorKeyEnc[9] << 0x08) & 0xff00;
        old |= (sock.client.newXorKeyEnc[10] << 0x10) & 0xff0000;
        old |= (sock.client.newXorKeyEnc[11] << 0x18) & 0xff000000;

        old += size;

        sock.client.newXorKeyEnc[8] = ((old >> 0x00) & 0xff);
        sock.client.newXorKeyEnc[9] = ((old >> 0x08) & 0xff);
        sock.client.newXorKeyEnc[10] = ((old >> 0x10) & 0xff);
        sock.client.newXorKeyEnc[11] = ((old >> 0x18) & 0xff);

    } catch (ex) {
        console.log(ex);
    }
}

crypto.generateNewKey = function () {
    try {
        var key = new Uint8Array(16);

        // randomize the 8 first bytes
        for (var j = 0; j < key.length; j++) {
            key[j] = crypto.randomInteger(1, 254);
        }

        // the last 8 bytes are static
        key[8] = 0xc8;
        key[9] = 0x27;
        key[10] = 0x93;
        key[11] = 0x01;
        key[12] = 0xa1;
        key[13] = 0x6c;
        key[14] = 0x31;
        key[15] = 0x97;
        return key;

    } catch (ex) {
        console.log(ex);
    }
    return [];
}

crypto.appendChecksum = function (raw, offset, size) {
    try {

        var chksum = 0;
        var count = size - 4;
        var ecx;
        var i;

        for (i = offset; i < count; i += 4) {
            ecx = (raw[i] << 0x00) & 0xff;
            ecx |= (raw[i + 1] << 0x08) & 0xff00;
            ecx |= (raw[i + 2] << 0x10) & 0xff0000;
            ecx |= (raw[i + 3] << 0x18) & 0xff000000;

            chksum ^= ecx;
        }

        ecx = (raw[i] >> 0x00) & 0xff;
        ecx |= (raw[i + 1] << 0x08) & 0xff00;
        ecx |= (raw[i + 2] << 0x10) & 0xff0000;
        ecx |= (raw[i + 3] << 0x18) & 0xff000000;

        raw[i] = ((chksum >> 0x00) & 0xff);
        raw[i + 1] = ((chksum >> 0x08) & 0xff);
        raw[i + 2] = ((chksum >> 0x10) & 0xff);
        raw[i + 3] = ((chksum >> 0x18) & 0xff);
        return raw;

    } catch (ex) {
        console.log(ex);
    }
    return [];
}

crypto.newPubKey = function () {
    try {
        var ret = new Uint8Array(16);
        for (var i = 0; i < 16; i++) {
            ret[i] = (parseInt((Math.random() * 50).toFixed(0)));
        }
        return ret;
    } catch (ex) {
        console.log(ex);
    }
    return [];
}

crypto.encXORPass = function (raw, offset, size, key) {
    try {

        var stop = size - 8;
        var pos = 4 + offset;
        var edx;
        var ecx = key; // Initial xor key

        while (pos < stop) {
            edx = (raw[pos] & 0xFF) << 0;
            edx |= (raw[pos + 1] & 0xFF) << 8;
            edx |= (raw[pos + 2] & 0xFF) << 16;
            edx |= (raw[pos + 3] & 0xFF) << 24;

            ecx += edx;

            edx ^= ecx;

            raw[pos++] = ((edx >> 0) & 0xFF);
            raw[pos++] = ((edx >> 8) & 0xFF);
            raw[pos++] = ((edx >> 16) & 0xFF);
            raw[pos++] = ((edx >> 24) & 0xFF);
        }

        raw[pos++] = ((ecx >> 0) & 0xFF);
        raw[pos++] = ((ecx >> 8) & 0xFF);
        raw[pos++] = ((ecx >> 16) & 0xFF);
        raw[pos++] = ((ecx >> 24) & 0xFF);
        return raw;

    } catch (ex) {
        console.log(ex);
    }
    return [];
}

module.exports = crypto;<commit_msg>MBTL packet - 14<commit_after>var crypto = {}

crypto.randomInteger = function (min, max) {
    var rand = min - 0.5 + Math.random() * (max - min + 1)
    rand = Math.round(rand);
    return rand;
}

crypto.decrypt = function (sock, raw, offset, size) {
    try {

        var temp = 0;
        for (var i = 0; i < size; i++) {
            var temp2 = raw[offset + i] & 0xFF;
            if (i == 8) {
                console.log(temp2.toString(16) + ' ' + sock.client.newXorKeyDec[i & 15].toString(16) + ' ' + temp);
            }
            raw[offset + i] = (temp2 ^ sock.client.newXorKeyDec[i & 15] ^ temp) & 0xff;
            if (i == 8) {
                console.log((raw[offset + i]).toString(16));
            }
            temp = temp2;
        }

        var old = (sock.client.newXorKeyDec[8] << 0x00) & 0xff;
        old |= (sock.client.newXorKeyDec[9] << 0x08) & 0xff00;
        old |= (sock.client.newXorKeyDec[10] << 0x10) & 0xff0000;
        old |= (sock.client.newXorKeyDec[11] << 0x18) & 0xff000000;

        old += size;

        sock.client.newXorKeyDec[8] = ((old >> 0) & 0xff);
        sock.client.newXorKeyDec[9] = ((old >> 0x08) & 0xff);
        sock.client.newXorKeyDec[10] = ((old >> 0x10) & 0xff);
        sock.client.newXorKeyDec[11] = ((old >> 0x18) & 0xff);

    } catch (ex) {
        console.log(ex);
    }
}

crypto.encrypt = function (sock, raw, offset, size) {
    try {

        var temp = 0;
        for (var i = 0; i < size; i++) {
            var temp2 = raw[offset + i] & 0xFF;
            temp = (temp2 ^ sock.client.newXorKeyEnc[i & 15] ^ temp) & 0xff;
            raw[offset + i] = temp;
        }

        var old = (sock.client.newXorKeyEnc[8] << 0x00) & 0xff;
        old |= (sock.client.newXorKeyEnc[9] << 0x08) & 0xff00;
        old |= (sock.client.newXorKeyEnc[10] << 0x10) & 0xff0000;
        old |= (sock.client.newXorKeyEnc[11] << 0x18) & 0xff000000;

        old += size;

        sock.client.newXorKeyEnc[8] = ((old >> 0x00) & 0xff);
        sock.client.newXorKeyEnc[9] = ((old >> 0x08) & 0xff);
        sock.client.newXorKeyEnc[10] = ((old >> 0x10) & 0xff);
        sock.client.newXorKeyEnc[11] = ((old >> 0x18) & 0xff);

    } catch (ex) {
        console.log(ex);
    }
}

crypto.generateNewKey = function () {
    try {
        var key = new Uint8Array(16);

        // randomize the 8 first bytes
        for (var j = 0; j < key.length; j++) {
            key[j] = crypto.randomInteger(1, 254);
        }

        // the last 8 bytes are static
        key[8] = 0xc8;
        key[9] = 0x27;
        key[10] = 0x93;
        key[11] = 0x01;
        key[12] = 0xa1;
        key[13] = 0x6c;
        key[14] = 0x31;
        key[15] = 0x97;
        return key;

    } catch (ex) {
        console.log(ex);
    }
    return [];
}

crypto.appendChecksum = function (raw, offset, size) {
    try {

        var chksum = 0;
        var count = size - 4;
        var ecx;
        var i;

        for (i = offset; i < count; i += 4) {
            ecx = (raw[i] << 0x00) & 0xff;
            ecx |= (raw[i + 1] << 0x08) & 0xff00;
            ecx |= (raw[i + 2] << 0x10) & 0xff0000;
            ecx |= (raw[i + 3] << 0x18) & 0xff000000;

            chksum ^= ecx;
        }

        ecx = (raw[i] >> 0x00) & 0xff;
        ecx |= (raw[i + 1] << 0x08) & 0xff00;
        ecx |= (raw[i + 2] << 0x10) & 0xff0000;
        ecx |= (raw[i + 3] << 0x18) & 0xff000000;

        raw[i] = ((chksum >> 0x00) & 0xff);
        raw[i + 1] = ((chksum >> 0x08) & 0xff);
        raw[i + 2] = ((chksum >> 0x10) & 0xff);
        raw[i + 3] = ((chksum >> 0x18) & 0xff);
        return raw;

    } catch (ex) {
        console.log(ex);
    }
    return [];
}

crypto.newPubKey = function () {
    try {
        var ret = new Uint8Array(16);
        for (var i = 0; i < 16; i++) {
            ret[i] = (parseInt((Math.random() * 50).toFixed(0)));
        }
        return ret;
    } catch (ex) {
        console.log(ex);
    }
    return [];
}

crypto.encXORPass = function (raw, offset, size, key) {
    try {

        var stop = size - 8;
        var pos = 4 + offset;
        var edx;
        var ecx = key; // Initial xor key

        while (pos < stop) {
            edx = (raw[pos] & 0xFF) << 0;
            edx |= (raw[pos + 1] & 0xFF) << 8;
            edx |= (raw[pos + 2] & 0xFF) << 16;
            edx |= (raw[pos + 3] & 0xFF) << 24;

            ecx += edx;

            edx ^= ecx;

            raw[pos++] = ((edx >> 0) & 0xFF);
            raw[pos++] = ((edx >> 8) & 0xFF);
            raw[pos++] = ((edx >> 16) & 0xFF);
            raw[pos++] = ((edx >> 24) & 0xFF);
        }

        raw[pos++] = ((ecx >> 0) & 0xFF);
        raw[pos++] = ((ecx >> 8) & 0xFF);
        raw[pos++] = ((ecx >> 16) & 0xFF);
        raw[pos++] = ((ecx >> 24) & 0xFF);
        return raw;

    } catch (ex) {
        console.log(ex);
    }
    return [];
}

module.exports = crypto;<|endoftext|>


<commit_before>/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/

export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'InitiatePlaybookRun', 'GetBasePath', 'Alert',
function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun, GetBasePath, Alert) {
    var val = {
        // the playbook_on_stats event returns the count data in a weird format.
        // format to what we need!
        getCountsFromStatsEvent: function(event_data) {
            var hosts = {},
                hostsArr;

            // iterate over the event_data and populate an object with hosts
            // and their status data
            Object.keys(event_data).forEach(key => {
                // failed passes boolean not integer
                if (key === "failed") {
                    // array of hosts from failed type
                    hostsArr = Object.keys(event_data[key]);
                    hostsArr.forEach(host => {
                        if (!hosts[host]) {
                            // host has not been added to hosts object
                            // add now
                            hosts[host] = {};
                        }

                        hosts[host][key] = event_data[key][host];
                    });
                } else {
                    // array of hosts from each type ("changed", "dark", etc.)
                    hostsArr = Object.keys(event_data[key]);
                    hostsArr.forEach(host => {
                        if (!hosts[host]) {
                            // host has not been added to hosts object
                            // add now
                            hosts[host] = {};
                        }

                        if (!hosts[host][key]) {
                            // host doesn't have key
                            hosts[host][key] = 0;
                        }
                        hosts[host][key] += event_data[key][host];
                    });
                }
            });

            // use the hosts data populate above to get the count
            var count = {
                ok : _.filter(hosts, function(o){
                    return !o.failures && !o.changed && o.ok > 0;
                }),
                skipped : _.filter(hosts, function(o){
                    return o.skipped > 0;
                }),
                unreachable : _.filter(hosts, function(o){
                    return o.dark > 0;
                }),
                failures : _.filter(hosts, function(o){
                    return o.failed === true;
                }),
                changed : _.filter(hosts, function(o){
                    return o.changed > 0;
                })
            };

            // turn the count into an actual count, rather than a list of host
            // names
            Object.keys(count).forEach(key => {
                count[key] = count[key].length;
            });

            return count;
        },
        // rest call to grab previously complete job_events
        getEvents: function(url) {
            var val = $q.defer();

            Rest.setUrl(url);
            Rest.get()
                .success(function(data) {
                    val.resolve({results: data.results,
                        next: data.next});
                })
                .error(function(obj, status) {
                    ProcessErrors(null, obj, status, null, {
                        hdr: 'Error!',
                        msg: `Could not get job events.
                            Returned status: ${status}`
                    });
                    val.reject(obj);
                });

            return val.promise;
        },
        deleteJob: function(job) {
            Prompt({
                hdr: 'Delete Job',
                body: `

                        Are you sure you want to delete the job below?

                        #${job.id} ${$filter('sanitize')(job.name)}

`,
                action: function() {
                    Wait('start');
                    Rest.setUrl(job.url);
                    Rest.destroy()
                        .success(function() {
                            Wait('stop');
                            $('#prompt-modal').modal('hide');
                            $state.go('jobs');
                        })
                        .error(function(obj, status) {
                            Wait('stop');
                            $('#prompt-modal').modal('hide');
                            ProcessErrors(null, obj, status, null, {
                                hdr: 'Error!',
                                msg: `Could not delete job.
                                    Returned status: ${status}`
                            });
                        });
                },
                actionText: 'DELETE'
            });
        },
        cancelJob: function(job) {
            var doCancel = function() {
                Rest.setUrl(job.url + 'cancel');
                Rest.post({})
                    .success(function() {
                        Wait('stop');
                        $('#prompt-modal').modal('hide');
                    })
                    .error(function(obj, status) {
                        Wait('stop');
                        $('#prompt-modal').modal('hide');
                        ProcessErrors(null, obj, status, null, {
                            hdr: 'Error!',
                            msg: `Could not cancel job.
                                Returned status: ${status}`
                        });
                    });
            };

            Prompt({
                hdr: 'Cancel Job',
                body: `

                        Are you sure you want to cancel the job below?

                        #${job.id} ${$filter('sanitize')(job.name)}

`,
                action: function() {
                    Wait('start');
                    Rest.setUrl(job.url + 'cancel');
                    Rest.get()
                        .success(function(data) {
                            if (data.can_cancel === true) {
                                doCancel();
                            } else {
                                $('#prompt-modal').modal('hide');
                                ProcessErrors(null, data, null, null, {
                                    hdr: 'Error!',
                                    msg: `Job has completed,
                                        unabled to be canceled.`
                                });
                            }
                        });
                },
                actionText: 'PROCEED'
            });
        },
        relaunchJob: function(scope) {
            InitiatePlaybookRun({ scope: scope, id: scope.job.id,
                relaunch: true });
        },
        getJobData: function(id){
            var val = $q.defer();

            Rest.setUrl(GetBasePath('jobs') + id );
            Rest.get()
                .then(function(data) {
                    val.resolve(data.data);
                }, function(data) {
                    val.reject(data);

                    if (data.status === 404) {
                        Alert('Job Not Found', 'Cannot find job.', 'alert-info');
                    } else if (data.status === 403) {
                        Alert('Insufficient Permissions', 'You do not have permission to view this job.', 'alert-info');
                    }

                    $state.go('jobs');
                });

            return val.promise;
        }
    };
    return val;
}];
<commit_msg>fix get failures count logic<commit_after>/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/

export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'InitiatePlaybookRun', 'GetBasePath', 'Alert',
function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun, GetBasePath, Alert) {
    var val = {
        // the playbook_on_stats event returns the count data in a weird format.
        // format to what we need!
        getCountsFromStatsEvent: function(event_data) {
            var hosts = {},
                hostsArr;

            // iterate over the event_data and populate an object with hosts
            // and their status data
            Object.keys(event_data).forEach(key => {
                // failed passes boolean not integer
                if (key === "changed" ||
                    key === "dark" ||
                    key === "failures" ||
                    key === "ok" ||
                    key === "skipped") {
                    // array of hosts from each type ("changed", "dark", etc.)
                    hostsArr = Object.keys(event_data[key]);
                    hostsArr.forEach(host => {
                        if (!hosts[host]) {
                            // host has not been added to hosts object
                            // add now
                            hosts[host] = {};
                        }

                        if (!hosts[host][key]) {
                            // host doesn't have key
                            hosts[host][key] = 0;
                        }
                        hosts[host][key] += event_data[key][host];
                    });
                }
            });

            // use the hosts data populate above to get the count
            var count = {
                ok : _.filter(hosts, function(o){
                    return !o.failures && !o.changed && o.ok > 0;
                }),
                skipped : _.filter(hosts, function(o){
                    return o.skipped > 0;
                }),
                unreachable : _.filter(hosts, function(o){
                    return o.dark > 0;
                }),
                failures : _.filter(hosts, function(o){
                    return o.failures > 0;
                }),
                changed : _.filter(hosts, function(o){
                    return o.changed > 0;
                })
            };

            // turn the count into an actual count, rather than a list of host
            // names
            Object.keys(count).forEach(key => {
                count[key] = count[key].length;
            });

            return count;
        },
        // rest call to grab previously complete job_events
        getEvents: function(url) {
            var val = $q.defer();

            Rest.setUrl(url);
            Rest.get()
                .success(function(data) {
                    val.resolve({results: data.results,
                        next: data.next});
                })
                .error(function(obj, status) {
                    ProcessErrors(null, obj, status, null, {
                        hdr: 'Error!',
                        msg: `Could not get job events.
                            Returned status: ${status}`
                    });
                    val.reject(obj);
                });

            return val.promise;
        },
        deleteJob: function(job) {
            Prompt({
                hdr: 'Delete Job',
                body: `

                        Are you sure you want to delete the job below?

                        #${job.id} ${$filter('sanitize')(job.name)}

`,
                action: function() {
                    Wait('start');
                    Rest.setUrl(job.url);
                    Rest.destroy()
                        .success(function() {
                            Wait('stop');
                            $('#prompt-modal').modal('hide');
                            $state.go('jobs');
                        })
                        .error(function(obj, status) {
                            Wait('stop');
                            $('#prompt-modal').modal('hide');
                            ProcessErrors(null, obj, status, null, {
                                hdr: 'Error!',
                                msg: `Could not delete job.
                                    Returned status: ${status}`
                            });
                        });
                },
                actionText: 'DELETE'
            });
        },
        cancelJob: function(job) {
            var doCancel = function() {
                Rest.setUrl(job.url + 'cancel');
                Rest.post({})
                    .success(function() {
                        Wait('stop');
                        $('#prompt-modal').modal('hide');
                    })
                    .error(function(obj, status) {
                        Wait('stop');
                        $('#prompt-modal').modal('hide');
                        ProcessErrors(null, obj, status, null, {
                            hdr: 'Error!',
                            msg: `Could not cancel job.
                                Returned status: ${status}`
                        });
                    });
            };

            Prompt({
                hdr: 'Cancel Job',
                body: `

                        Are you sure you want to cancel the job below?

                        #${job.id} ${$filter('sanitize')(job.name)}

`,
                action: function() {
                    Wait('start');
                    Rest.setUrl(job.url + 'cancel');
                    Rest.get()
                        .success(function(data) {
                            if (data.can_cancel === true) {
                                doCancel();
                            } else {
                                $('#prompt-modal').modal('hide');
                                ProcessErrors(null, data, null, null, {
                                    hdr: 'Error!',
                                    msg: `Job has completed,
                                        unabled to be canceled.`
                                });
                            }
                        });
                },
                actionText: 'PROCEED'
            });
        },
        relaunchJob: function(scope) {
            InitiatePlaybookRun({ scope: scope, id: scope.job.id,
                relaunch: true });
        },
        getJobData: function(id){
            var val = $q.defer();

            Rest.setUrl(GetBasePath('jobs') + id );
            Rest.get()
                .then(function(data) {
                    val.resolve(data.data);
                }, function(data) {
                    val.reject(data);

                    if (data.status === 404) {
                        Alert('Job Not Found', 'Cannot find job.', 'alert-info');
                    } else if (data.status === 403) {
                        Alert('Insufficient Permissions', 'You do not have permission to view this job.', 'alert-info');
                    }

                    $state.go('jobs');
                });

            return val.promise;
        }
    };
    return val;
}];
<|endoftext|>


<commit_before>// require jquery.ba-url
//
//= require social_stream/callback

SocialStream.Contact = (function($, SS, undefined) {
  var callback = new SS.Callback();

  var getForms = function(id) {
    return $('[data-contact_id="' + id + '"]');
  };

  var initTabs = function() {
    $('.contacts ul.nav-tabs a').click(loadTab);
  };

  var loadTab = function() {
    var tab = $(this);

    if (tab.attr('data-loaded'))
      return;

    $.ajax({
      url: tab.attr('data-path'),
      data: {
        type: tab.attr('href').replace('#', ''),
        q: $('#contact-filter').val()
      },
      dataType: 'html',
      type: 'GET',
      success: function(data) {
        $(tab.attr('href')).find('.contact-list').html(data);
        tab.attr('data-loaded', 'true');
        callback.handlers.index();
      }
    });
  };

  var initMultipleButtons = function() {
    $('.edit_contact select[name*="relation_ids"]').multiselect({
      buttonClass: 'btn btn-small',
      buttonText: relationSelectText
    });

    // Forms
    storeFormValues($('form.edit_contact'));

    $('form.edit_contact ul.dropdown-menu input[type="checkbox"]').change(function() {
      evalFormStatus(this);
    });

    $('form.edit_contact button.dropdown-toggle').on('click.dropdown.data-api', saveForms);
  };

  var relationSelectText = function(options) {
    var msg;

    if (options.length === 0) {
      msg = I18n.t('contact.new.button.zero');
    }
    else {
      msg = I18n.t('contact.new.button', { count: options.length });
    }

    return msg + '';
  };

  var saveForms = function() {
    $('form.edit_contact[data-status="changed"]').each(function(i, form) {
      var contactId = $(form).attr('data-contact_id');
      var contacts = $('form[data-contact_id="' + contactId + '"]');

      $('button', contacts).attr('data-loading-text', I18n.t('contact.saving'));
      $('button', contacts).button('loading');

      if ($('option:selected', form).length > 0) {
        $(form).submit();
      } else {
        if ( confirm(I18n.t('contact.confirm_delete')) ) {
          $(form).submit();
        } else {
          resetForms({ id: contactId });
        }
      }
    });
  };

  var storeFormValues = function(el) {
    $(el).each(function() {
      $(this).data('relations', getInputValues($(this)));
    });
  };

  var restoreFormValues = function(el) {
    var values = $(el).data('relations');
    var select = $('select[name*="relation_ids"]', el);

    $('option', select).each(function() {
      $(this).attr('selected', $.inArray($(this).val(), values) >= 0);

    });

    select.multiselect('refresh');
  };

  // Dictate if some form has changed its status
  var evalFormStatus = function(el) {
    var form = $(el).closest('form');

    var orig = $(form).data('relations');
    var neww = getInputValues(form);

    if ($(orig).not(neww).length === 0 && $(neww).not(orig).length === 0) {
      $(form).removeAttr('data-status');
    } else {
      $(form).attr('data-status', 'changed');
    }
  };

  var getInputValues = function(form) {
    return $('ul.dropdown-menu input[type="checkbox"]', form).
      map(function() {
      if ($(this).is(':checked'))
        return $(this).val();
      });
  };

  var setInputValues = function(form) {
    return $('ul.dropdown-menu input[type="checkbox"]', form).
      map(function() {
      if ($(this).is(':checked'))
        return $(this).val();
    });
  };

  var initFilter = function() {
    $('.contact-filter').on('input', filter);
  };

  var filter = function() {
    var q = $(this).val();
    var currentTab = $('.contacts .tab-pane.active');

    $('#contacts-loading').show();

    $.ajax({
      data: {
        q: q,
        type: currentTab.attr('id')
      },
      dataType: 'html',
      type: 'GET',
      success: function(data) {
        $('#contacts-loading').hide();
         currentTab.find('.contact-list').html(data);
        callback.handlers.index();
      }
    });
  };

  var hideLoading = function() {
    $('#contacts-loading').hide();
  };

  var initNewGroupModal = function() {
    $('.new_group-modal-link').attr('href', '#new_group-modal');
  };

  var initContactFormsHtmlListener = function() {
    $('html').on('click.dropdown.data-api', saveForms);
  };

  // new_ callbacks

  var initActorSelect2 = function() {
    SS.Actor.select2('input[name="actors"]');
  };

  var initRelationSelect2 = function() {
    $('select[name="relations[]"]').select2();
  };

  // update callbacks

  var updateForms = function(options) {
    var forms = getForms(options.id);

    storeFormValues(forms);
    $('button', forms).data('resetText', relationSelectText($('option:selected', forms)));
    forms.removeAttr('data-status');
    $('button', forms).button('reset');
  };

  var resetForms = function(options) {
    var forms = getForms(options.id);

    restoreFormValues(forms);
    forms.removeAttr('data-status');
    $('button', forms).data('resetText', relationSelectText($('option:selected', forms)));
    $('button', forms).button('reset');
  };

  // Replace contact in suggestions and pendings
  var replaceContact = function(options) {
    $('[data-contact_id="' + options.id + '"]').each(function(i, el) {
      $.each([ 'suggestions', 'pendings' ], function(i, section) {
        var sectionId = '#' + section;

        if ($(el).closest(sectionId).length > 0) {
          updateTemplate(el, $(sectionId).attr('data-path'));
        }
      });
    });
  };

  var updateTemplate = function(el, path) {
    var contact = $(el).closest('.contact');

    $.ajax({
      url: path,
      dataType: 'html',
      type: 'GET',
      success: function(data) {
        $(contact).fadeOut('slow', function() {
          $(data).replaceAll(contact).fadeIn();

          initMultipleButtons();
        });
      }
    });
  };

  var checkAndHideContact = function(options) {
    var forms = getForms(options.id);

    if ($('option:selected', forms).length === 0) {
      forms.closest('.contact').hide('slow');
    }
  };

  var hideContact = function(options) {
    getForms(options.id).closest('.contact').hide('slow');
  };

  // Select2
  var select2 = function(selector) {
    $(selector).select2({
      multiple: true,
      ajax: {
        url: $(selector).attr('data-path'),
        dataType: 'json',
        data: function(term, page) {
          return { q: term, page: page };
        },
        results: function(data, page) {
          return { results: data };
        }
      },
      id: function(object) { return object.id.toString(); },
      formatResult: select2FormatResult,
      formatSelection: select2FormatSelection,
      initSelection: select2InitSelection
    });
  };

  var select2FormatSelection = function(object, container, query) {
    return object.name;
  };

  var select2FormatResult = function(object, container) {
    return ' ' + object.name;
  };

  var select2InitSelection = function(element, callback) {
    callback([ { id: element.val(), name: element.attr('data-recipient-name') } ]);
  };

  var initSimpleButtons = function(){
    $(".following-button").mouseenter(function(){
      $(this).hide();
      $(this).siblings(".unfollow-button").show();
    });

    $(".unfollow-button").mouseleave(function(){
      $(this).hide();
      $(this).siblings(".following-button").show();
    });

    $(".unfollow-button").hide();
  };

  callback.register('index',
                    initTabs,
                    initMultipleButtons,
                    initSimpleButtons,
                    initFilter,
                    initNewGroupModal,
                    hideLoading);

  callback.register('new_',
                    initActorSelect2,
                    initRelationSelect2);

  callback.register('update',
                    updateForms,
                    replaceContact,
                    initSimpleButtons,
                    checkAndHideContact);

  callback.register('destroy',
                    initSimpleButtons,
                    hideContact);

  // FIXME There is probably a more efficient way to do this..
  $(function() {
    initMultipleButtons();
    initSimpleButtons();
    initContactFormsHtmlListener();
  });

  return callback.extend({
    select2: select2
  });
})(jQuery, SocialStream);
<commit_msg>Close select2 menus along with add contact modal<commit_after>// require jquery.ba-url
//
//= require social_stream/callback

SocialStream.Contact = (function($, SS, undefined) {
  var callback = new SS.Callback();

  var getForms = function(id) {
    return $('[data-contact_id="' + id + '"]');
  };

  var initTabs = function() {
    $('.contacts ul.nav-tabs a').click(loadTab);
  };

  var loadTab = function() {
    var tab = $(this);

    if (tab.attr('data-loaded'))
      return;

    $.ajax({
      url: tab.attr('data-path'),
      data: {
        type: tab.attr('href').replace('#', ''),
        q: $('#contact-filter').val()
      },
      dataType: 'html',
      type: 'GET',
      success: function(data) {
        $(tab.attr('href')).find('.contact-list').html(data);
        tab.attr('data-loaded', 'true');
        callback.handlers.index();
      }
    });
  };

  var initMultipleButtons = function() {
    $('.edit_contact select[name*="relation_ids"]').multiselect({
      buttonClass: 'btn btn-small',
      buttonText: relationSelectText
    });

    // Forms
    storeFormValues($('form.edit_contact'));

    $('form.edit_contact ul.dropdown-menu input[type="checkbox"]').change(function() {
      evalFormStatus(this);
    });

    $('form.edit_contact button.dropdown-toggle').on('click.dropdown.data-api', saveForms);
  };

  var relationSelectText = function(options) {
    var msg;

    if (options.length === 0) {
      msg = I18n.t('contact.new.button.zero');
    }
    else {
      msg = I18n.t('contact.new.button', { count: options.length });
    }

    return msg + '';
  };

  var saveForms = function() {
    $('form.edit_contact[data-status="changed"]').each(function(i, form) {
      var contactId = $(form).attr('data-contact_id');
      var contacts = $('form[data-contact_id="' + contactId + '"]');

      $('button', contacts).attr('data-loading-text', I18n.t('contact.saving'));
      $('button', contacts).button('loading');

      if ($('option:selected', form).length > 0) {
        $(form).submit();
      } else {
        if ( confirm(I18n.t('contact.confirm_delete')) ) {
          $(form).submit();
        } else {
          resetForms({ id: contactId });
        }
      }
    });
  };

  var storeFormValues = function(el) {
    $(el).each(function() {
      $(this).data('relations', getInputValues($(this)));
    });
  };

  var restoreFormValues = function(el) {
    var values = $(el).data('relations');
    var select = $('select[name*="relation_ids"]', el);

    $('option', select).each(function() {
      $(this).attr('selected', $.inArray($(this).val(), values) >= 0);

    });

    select.multiselect('refresh');
  };

  // Dictate if some form has changed its status
  var evalFormStatus = function(el) {
    var form = $(el).closest('form');

    var orig = $(form).data('relations');
    var neww = getInputValues(form);

    if ($(orig).not(neww).length === 0 && $(neww).not(orig).length === 0) {
      $(form).removeAttr('data-status');
    } else {
      $(form).attr('data-status', 'changed');
    }
  };

  var getInputValues = function(form) {
    return $('ul.dropdown-menu input[type="checkbox"]', form).
      map(function() {
      if ($(this).is(':checked'))
        return $(this).val();
      });
  };

  var setInputValues = function(form) {
    return $('ul.dropdown-menu input[type="checkbox"]', form).
      map(function() {
      if ($(this).is(':checked'))
        return $(this).val();
    });
  };

  var initFilter = function() {
    $('.contact-filter').on('input', filter);
  };

  var filter = function() {
    var q = $(this).val();
    var currentTab = $('.contacts .tab-pane.active');

    $('#contacts-loading').show();

    $.ajax({
      data: {
        q: q,
        type: currentTab.attr('id')
      },
      dataType: 'html',
      type: 'GET',
      success: function(data) {
        $('#contacts-loading').hide();
         currentTab.find('.contact-list').html(data);
        callback.handlers.index();
      }
    });
  };

  var hideLoading = function() {
    $('#contacts-loading').hide();
  };

  var initNewGroupModal = function() {
    $('.new_group-modal-link').attr('href', '#new_group-modal');
  };

  var initContactFormsHtmlListener = function() {
    $('html').on('click.dropdown.data-api', saveForms);
  };

  // new_ callbacks

  var initHideModal = function() {
    $('#add-contact-modal').on('hide', function () {
      $('input[name="actors"]').select2('close');
      $('select[name="relations[]"]').select2('close');
    });
  };

  var initActorSelect2 = function() {
    SS.Actor.select2('input[name="actors"]');
  };

  var initRelationSelect2 = function() {
    $('select[name="relations[]"]').select2();
  };

  // update callbacks

  var updateForms = function(options) {
    var forms = getForms(options.id);

    storeFormValues(forms);
    $('button', forms).data('resetText', relationSelectText($('option:selected', forms)));
    forms.removeAttr('data-status');
    $('button', forms).button('reset');
  };

  var resetForms = function(options) {
    var forms = getForms(options.id);

    restoreFormValues(forms);
    forms.removeAttr('data-status');
    $('button', forms).data('resetText', relationSelectText($('option:selected', forms)));
    $('button', forms).button('reset');
  };

  // Replace contact in suggestions and pendings
  var replaceContact = function(options) {
    $('[data-contact_id="' + options.id + '"]').each(function(i, el) {
      $.each([ 'suggestions', 'pendings' ], function(i, section) {
        var sectionId = '#' + section;

        if ($(el).closest(sectionId).length > 0) {
          updateTemplate(el, $(sectionId).attr('data-path'));
        }
      });
    });
  };

  var updateTemplate = function(el, path) {
    var contact = $(el).closest('.contact');

    $.ajax({
      url: path,
      dataType: 'html',
      type: 'GET',
      success: function(data) {
        $(contact).fadeOut('slow', function() {
          $(data).replaceAll(contact).fadeIn();

          initMultipleButtons();
        });
      }
    });
  };

  var checkAndHideContact = function(options) {
    var forms = getForms(options.id);

    if ($('option:selected', forms).length === 0) {
      forms.closest('.contact').hide('slow');
    }
  };

  var hideContact = function(options) {
    getForms(options.id).closest('.contact').hide('slow');
  };

  // Select2
  var select2 = function(selector) {
    $(selector).select2({
      multiple: true,
      ajax: {
        url: $(selector).attr('data-path'),
        dataType: 'json',
        data: function(term, page) {
          return { q: term, page: page };
        },
        results: function(data, page) {
          return { results: data };
        }
      },
      id: function(object) { return object.id.toString(); },
      formatResult: select2FormatResult,
      formatSelection: select2FormatSelection,
      initSelection: select2InitSelection
    });
  };

  var select2FormatSelection = function(object, container, query) {
    return object.name;
  };

  var select2FormatResult = function(object, container) {
    return ' ' + object.name;
  };

  var select2InitSelection = function(element, callback) {
    callback([ { id: element.val(), name: element.attr('data-recipient-name') } ]);
  };

  var initSimpleButtons = function(){
    $(".following-button").mouseenter(function(){
      $(this).hide();
      $(this).siblings(".unfollow-button").show();
    });

    $(".unfollow-button").mouseleave(function(){
      $(this).hide();
      $(this).siblings(".following-button").show();
    });

    $(".unfollow-button").hide();
  };

  callback.register('index',
                    initTabs,
                    initMultipleButtons,
                    initSimpleButtons,
                    initFilter,
                    initNewGroupModal,
                    hideLoading);

  callback.register('new_',
                    initHideModal,
                    initActorSelect2,
                    initRelationSelect2);

  callback.register('update',
                    updateForms,
                    replaceContact,
                    initSimpleButtons,
                    checkAndHideContact);

  callback.register('destroy',
                    initSimpleButtons,
                    hideContact);

  // FIXME There is probably a more efficient way to do this..
  $(function() {
    initMultipleButtons();
    initSimpleButtons();
    initContactFormsHtmlListener();
  });

  return callback.extend({
    select2: select2
  });
})(jQuery, SocialStream);
<|endoftext|>


<commit_before>var dynamicApp = angular.module('studyApp.DynamicCharts', ['ngRoute']);

dynamicApp.controller('dynamicChartsCtrl', function($scope, $interval) {
    $scope.chartVisible = 'bar';
	$scope.salesData = [
    {hour: 1,sales: 54},
    {hour: 2,sales: 66},
    {hour: 3,sales: 77},
    {hour: 4,sales: 70},
    {hour: 5,sales: 60},
    {hour: 6,sales: 63},
    {hour: 7,sales: 55},
    {hour: 8,sales: 47},
    {hour: 9,sales: 55},
    {hour: 10,sales: 30}
  ];

  $interval(function(){
  	var hour = $scope.salesData.length+1,
  	sales = Math.round(Math.random()*100);

  	$scope.salesData.push({
  		"hour": hour,
  		"sales": sales
  	});
  },1000,1000);
});

dynamicApp.directive('linearChart', function($parse, $window) {
    return {
        restrict: 'EA', // E = Element, A = Attribute, C = Class, M = Comment
        template: '<svg width="850" height="200"></svg>',
        link: function(scope, iElm, iAttrs, controller) {
            var salesDataToPlot = scope[iAttrs.chartData],
                padding = 20,
                pathClass = 'path',
                xScale, yScale, xAxisGen, yAxisGen, lineRender,
                d3 = $window.d3,
                rawSvg = iElm.find("svg")[0],
                svg = d3.select(rawSvg);

            //Watching the array if any changes occur.
            scope.$watchCollection("salesData", function(newData, oldData){
                salesDataToPlot = newData;
             	reDrawLineChart();
            });

            function setChartParams() {
                //Plotting X-Hour and Y-Sales.
                xScale = d3.scale.linear()
                    .domain([0, d3.max(salesDataToPlot, function(d) {
                        return d.hour;
                    })])
                    .range([padding + 5, rawSvg.clientWidth - padding]);

                yScale = d3.scale.linear()
                    .domain([0, d3.max(salesDataToPlot, function(d) {
                        return d.sales;
                    })])
                    .range([rawSvg.clientHeight - padding,0]);

                xAxisGen = d3.svg.axis()
                			.scale(xScale)
                			.orient("bottom")
                			.ticks(10);

                yAxisGen = d3.svg.axis()
                			.scale(yScale)
                			.orient("left")
                			.ticks(5);

                lineRender = d3.svg.line()
                			.x(function(d){
                				return xScale(d.hour);
                			})
                			.y(function(d){
                				return yScale(d.sales);
                			})
                			.interpolate("basis");
            }

            function drawLineChart(){
            	setChartParams();

            	svg.attr("class","parentSvg");

            	svg.append("svg:g")
            		.attr("class", "x axis")
            		.attr("transform", "translate(0,180)")
            		.call(xAxisGen);

            	svg.append("svg:g")
            		.attr("class", "y axis")
            		.attr("transform", "translate(20,0)")
            		.call(yAxisGen);

				svg.append("svg:path")
				    .attr({
				        d: lineRender(salesDataToPlot),
				        "stroke": "blue",
				        "stroke-width": 2,
				        "fill": "none",
				        "class": pathClass
				    });
            }

			function reDrawLineChart() {
			  setChartParams();
			  svg.selectAll("g.y.axis").call(yAxisGen);
			  svg.selectAll("g.x.axis").call(xAxisGen);

			  svg.selectAll("." + pathClass)
			     .attr({
			       d: lineRender(salesDataToPlot)
			     });
			}
            drawLineChart();
        }
    };
});

dynamicApp.directive('barChart', function($parse,$window){
    return {
        restrict: "EA",
        template: '<svg width="850" height="200"></svg>',
        link: function(scope, iElm, iAttrs, controller) {
               var salesDataToPlot = scope[iAttrs.chartData],
                padding = 20,
                pathClass = 'path',
                xScale, yScale, xAxisGen, yAxisGen, lineRender,
                d3 = $window.d3,
                rawSvg = iElm.find("svg")[0],
                svg = d3.select(rawSvg);

            function setbarChartParams() {
                //Plotting X-Hour and Y-Sales.
                xScale = d3.scale.linear()
                    .domain([0, d3.max(salesDataToPlot, function(d) {
                        return d.hour;
                    })])
                    .range([padding + 5, rawSvg.clientWidth - padding]);

                yScale = d3.scale.linear()
                    .domain([0, d3.max(salesDataToPlot, function(d) {
                        return d.sales;
                    })])
                    .range([rawSvg.clientHeight - padding,0]);

                xAxisGen = d3.svg.axis()
                            .scale(xScale)
                            .orient("bottom")
                            .ticks(10)
                            .tickSubdivide(true);

                yAxisGen = d3.svg.axis()
                            .scale(yScale)
                            .orient("left")
                            .ticks(5)
                            .tickSubdivide(true);
            }

            function drawLineChart(){
                setbarChartParams();

                svg.attr("class","parentSvg");

                svg.append("svg:g")
                    .attr("class", "x axis")
                    .attr("transform", "translate(0,180)")
                    .call(xAxisGen);

                svg.append("svg:g")
                    .attr("class", "y axis")
                    .attr("transform", "translate(20,0)")
                    .call(yAxisGen);

                svg.selectAll('rect')
                    .data(salesDataToPlot)
                    .enter()
                    .append('rect')
                    .attr('x', function(d) {
                      return xScale(d.hour);
                      })
                    .attr('y',function(d) {
                      return yScale(d.sales);
                      })
                    .attr('width', 20)
                    .attr('height', function(d){
                        return (rawSvg.clientHeight - yScale(d.sales))
                    })
                    .attr("transform","translate(0,-20)")
                    .attr('fill', '#6B6B6B')
                    .on('mouseover', function(d) {
                      d3.select(this)
                        .attr('fill', 'red');
                    })
                    .on('mouseout', function(d) {
                      d3.select(this)
                        .attr('fill', '#6B6B6B');
                    });
            }
            drawLineChart();
        }
    }
});<commit_msg>Made bar chart dynamic<commit_after>var dynamicApp = angular.module('studyApp.DynamicCharts', ['ngRoute']);

dynamicApp.controller('dynamicChartsCtrl', function($scope, $interval) {
    $scope.chartVisible = 'bar';
	$scope.salesData = [
    {hour: 1,sales: 54},
    {hour: 2,sales: 66},
    {hour: 3,sales: 77},
    {hour: 4,sales: 70},
    {hour: 5,sales: 60},
    {hour: 6,sales: 63},
    {hour: 7,sales: 55},
    {hour: 8,sales: 47},
    {hour: 9,sales: 55},
    {hour: 10,sales: 30}
  ];

  $interval(function(){
  	var hour = $scope.salesData.length+1,
  	sales = Math.round(Math.random()*100);

  	$scope.salesData.push({
  		"hour": hour,
  		"sales": sales
  	});
  },1000,1000);
});

dynamicApp.directive('linearChart', function($parse, $window) {
    return {
        restrict: 'EA', // E = Element, A = Attribute, C = Class, M = Comment
        template: '<svg width="850" height="200"></svg>',
        link: function(scope, iElm, iAttrs, controller) {
            var salesDataToPlot = scope[iAttrs.chartData],
                padding = 20,
                pathClass = 'path',
                xScale, yScale, xAxisGen, yAxisGen, lineRender,
                d3 = $window.d3,
                rawSvg = iElm.find("svg")[0],
                svg = d3.select(rawSvg);

            //Watching the array if any changes occur.
            scope.$watchCollection("salesData", function(newData, oldData){
                salesDataToPlot = newData;
             	reDrawLineChart();
            });

            function setlineChartParams() {
                //Plotting X-Hour and Y-Sales.
                xScale = d3.scale.linear()
                    .domain([0, d3.max(salesDataToPlot, function(d) {
                        return d.hour;
                    })])
                    .range([padding + 5, rawSvg.clientWidth - padding]);

                yScale = d3.scale.linear()
                    .domain([0, d3.max(salesDataToPlot, function(d) {
                        return d.sales;
                    })])
                    .range([rawSvg.clientHeight - padding,0]);

                xAxisGen = d3.svg.axis()
                			.scale(xScale)
                			.orient("bottom")
                			.ticks(10);

                yAxisGen = d3.svg.axis()
                			.scale(yScale)
                			.orient("left")
                			.ticks(5);

                lineRender = d3.svg.line()
                			.x(function(d){
                				return xScale(d.hour);
                			})
                			.y(function(d){
                				return yScale(d.sales);
                			})
                			.interpolate("basis");
            }

            function drawLineChart(){
            	setlineChartParams();

            	svg.attr("class","parentSvg");

            	svg.append("svg:g")
            		.attr("class", "x axis")
            		.attr("transform", "translate(0,180)")
            		.call(xAxisGen);

            	svg.append("svg:g")
            		.attr("class", "y axis")
            		.attr("transform", "translate(20,0)")
            		.call(yAxisGen);

				svg.append("svg:path")
				    .attr({
				        d: lineRender(salesDataToPlot),
				        "stroke": "blue",
				        "stroke-width": 2,
				        "fill": "none",
				        "class": pathClass
				    });
            }

			function reDrawLineChart() {
			  setlineChartParams();
			  svg.selectAll("g.y.axis").call(yAxisGen);
			  svg.selectAll("g.x.axis").call(xAxisGen);

			  svg.selectAll("." + pathClass)
			     .attr({
			       d: lineRender(salesDataToPlot)
			     });
			}
            drawLineChart();
        }
    };
});

dynamicApp.directive('barChart', function( $parse, $window){
    return {
        restrict: "EA",
        template: '<svg width="850" height="200"></svg>',
        link: function(scope, iElm, iAttrs, controller) {
               var salesDataToPlot = scope[iAttrs.chartData],
                padding = 20,
                rectClass = 'rect',
                xScale, yScale, xAxisGen, yAxisGen, barRender,
                d3 = $window.d3,
                rawSvg = iElm.find("svg")[0],
                svg = d3.select(rawSvg);

            //Watching the array if any changes occur.
            // scope.$watchCollection("salesData", function(newData, oldData){
            //     salesDataToPlot = newData;
            //     reDrawbarChart();
            // });

            function setbarChartParams() {
                //Plotting X-Hour and Y-Sales.
                xScale = d3.scale.linear()
                    .domain([0, d3.max(salesDataToPlot, function(d) {
                        return d.hour;
                    })])
                    .range([padding + 5, rawSvg.clientWidth - padding]);

                yScale = d3.scale.linear()
                    .domain([0, d3.max(salesDataToPlot, function(d) {
                        return d.sales;
                    })])
                    .range([rawSvg.clientHeight - padding,0]);

                xAxisGen = d3.svg.axis()
                            .scale(xScale)
                            .orient("bottom")
                            .ticks(10)
                            .tickSubdivide(true);

                yAxisGen = d3.svg.axis()
                            .scale(yScale)
                            .orient("left")
                            .ticks(5)
                            .tickSubdivide(true);
            }

            function drawBarChart(){
                setbarChartParams();

                svg.attr("class","parentSvg");

                svg.append("svg:g")
                    .attr("class", "x axis")
                    .attr("transform", "translate(0,180)")
                    .call(xAxisGen);

                svg.append("svg:g")
                    .attr("class", "y axis")
                    .attr("transform", "translate(20,0)")
                    .call(yAxisGen);

                barRender = function(){
                    svg.selectAll('rect').remove();
                    svg.selectAll('rect')
                    .data(salesDataToPlot)
                    .enter()
                    .append('rect')
                    .attr("class",rectClass)
                    .attr('x', function(d) {
                      return xScale(d.hour);
                      })
                    .attr('y',function(d) {
                      return yScale(d.sales);
                      })
                    .attr('width', 20)
                    .attr('height', function(d){
                        return (rawSvg.clientHeight - yScale(d.sales))
                    })
                    .attr("transform","translate(0,-20)")
                    .attr('fill', '#6B6B6B')
                    .on('mouseover', function(d) {
                      d3.select(this)
                        .attr('fill', 'red');
                    })
                    .on('mouseout', function(d) {
                      d3.select(this)
                        .attr('fill', '#6B6B6B');
                    });
                }
                barRender();
            }

            function reDrawbarChart() {
              setbarChartParams();
              svg.selectAll("g.y.axis").call(yAxisGen);
              svg.selectAll("g.x.axis").call(xAxisGen);

              barRender();
            }

            drawBarChart();
        }
    }
});<|endoftext|>


<commit_before>/*
   Mixin to enable scrolling from one anchor point to another
   within a same page.

   Mix the mixin into View classes like:
   e.g. App.YourView = Ember.View.extend(App.GoTo, {});

   And, In your template,

   Source

   Or,

   Source
 */
App.GoTo = Ember.Mixin.create({

    click: function(e) {
        var $target = $(e.target);
        if ($target.hasClass('goto')) {
            var anchor = $target.data('target') || $target.attr('rel');
            if (anchor) {
                this.goTo(anchor);
                e.preventDefault();
            }
        }
    },

    goTo: function(target) {
        $('html, body').stop().animate({
            scrollTop: $(target).offset().top - $('#header').height()
        }, 500);
    }
});

App.PageView = Ember.View.extend(App.GoTo, {
    templateName: 'page',

    classNames: 'page static-page'.w(),

    setup: function() {
        Ember.run.scheduleOnce('afterRender', this, function() {
            if (!Em.isNone(this.$())) {
                this.renderSections();
                this.bindEvents();
            }
        });
    }.observes('controller.body'),

    willDestroyElement: function() {
        this.unbindEvents();
    },

    bindEvents: function() {
        $(window).on('resize', $.proxy(this.renderSections, this));
    },

    unbindEvents: function() {
        $(window).off('resize', $.proxy(this.renderSections, this));
    },

    renderSections: function(e) {
        var windowHeight = $(window).height();

        this.$('.static-onepage-content').each(function() {
            // Reset first to get correct height
            $(this).css({
                'position' : 'relative',
                'height' : 'auto'
            });
            // Set static height for centering if not higher then section
            if ($(this).height() <= $(window).height() - 100) {
                $(this).css({
                    'height' : $(this).height() + 'px',
                    'position' : 'absolute'
                });
                // Set section height to window height
                $(this).closest( ".static-onepage-section" ).css('height', windowHeight + 'px');
            }
        });
    },

    didInsertElement: function(evt) {
        // Check if the content has any ember link attributes
        // This allows custom page content to specify linkTo targets
        var _this = this;
        this.$('[data-ember-link-to]').on('click', function (linkEvt) {
            var target = $(linkEvt.target),
                newRoute = target.data('emberLinkTo'),
                newRouteAttr = target.data('emberLinkToAttr'),
                router = _this.get('controller.target.router');

            router.transitionTo(newRoute, newRouteAttr);
        });

        this.$().find('.carousel').unslider({
            dots: true,
            fluid: true,
            delay: 10000
        });
    }
});
<commit_msg>use 'arg' rather than 'attr' in link-to helper for pages<commit_after>/*
   Mixin to enable scrolling from one anchor point to another
   within a same page.

   Mix the mixin into View classes like:
   e.g. App.YourView = Ember.View.extend(App.GoTo, {});

   And, In your template,

   Source

   Or,

   Source
 */
App.GoTo = Ember.Mixin.create({

    click: function(e) {
        var $target = $(e.target);
        if ($target.hasClass('goto')) {
            var anchor = $target.data('target') || $target.attr('rel');
            if (anchor) {
                this.goTo(anchor);
                e.preventDefault();
            }
        }
    },

    goTo: function(target) {
        $('html, body').stop().animate({
            scrollTop: $(target).offset().top - $('#header').height()
        }, 500);
    }
});

App.PageView = Ember.View.extend(App.GoTo, {
    templateName: 'page',

    classNames: 'page static-page'.w(),

    setup: function() {
        Ember.run.scheduleOnce('afterRender', this, function() {
            if (!Em.isNone(this.$())) {
                this.renderSections();
                this.bindEvents();
            }
        });
    }.observes('controller.body'),

    willDestroyElement: function() {
        this.unbindEvents();
    },

    bindEvents: function() {
        $(window).on('resize', $.proxy(this.renderSections, this));
    },

    unbindEvents: function() {
        $(window).off('resize', $.proxy(this.renderSections, this));
    },

    renderSections: function(e) {
        var windowHeight = $(window).height();

        this.$('.static-onepage-content').each(function() {
            // Reset first to get correct height
            $(this).css({
                'position' : 'relative',
                'height' : 'auto'
            });
            // Set static height for centering if not higher then section
            if ($(this).height() <= $(window).height() - 100) {
                $(this).css({
                    'height' : $(this).height() + 'px',
                    'position' : 'absolute'
                });
                // Set section height to window height
                $(this).closest( ".static-onepage-section" ).css('height', windowHeight + 'px');
            }
        });
    },

    didInsertElement: function(evt) {
        // Check if the content has any ember link attributes
        // This allows custom page content to specify linkTo targets
        var _this = this;
        this.$('[data-ember-link-to]').on('click', function (linkEvt) {
            var target = $(linkEvt.target),
                newRoute = target.data('emberLinkTo'),
                newRouteAttr = target.data('emberLinkToArg'),
                router = _this.get('controller.target.router');

            router.transitionTo(newRoute, newRouteAttr);
        });

        this.$().find('.carousel').unslider({
            dots: true,
            fluid: true,
            delay: 10000
        });
    }
});
<|endoftext|>


<commit_before>import ArticlePageLayout from 'components/ArticlePageLayout';
import { useRouter } from 'next/router';
import Head from 'next/head';
import getConfig from 'next/config';
import { t } from 'ttag';
import querystring from 'querystring';
import AppLayout from 'components/AppLayout';
import withData from 'lib/apollo';

const {
  publicRuntimeConfig: { PUBLIC_URL },
} = getConfig();

function ArticleListPage() {
  const { query } = useRouter();
  const queryString = querystring.stringify(query);

  return (
    <AppLayout>
      <Head>
        <title>{t`Dubious Messages`}</title>
        <link
          rel="alternate"
          type="application/rss+xml"
          href={`${PUBLIC_URL}/api/articles/rss2?${queryString}`}
        />
        <link
          rel="alternate"
          type="application/atom+xml"
          href={`${PUBLIC_URL}/api/articles/atom1?${queryString}`}
        />
      </Head>
      <ArticlePageLayout title={t`Dubious Messages`} page="articles" />
    </AppLayout>
  );
}

export default withData(ArticleListPage);
<commit_msg>[articles] replace ArticlePageLayout<commit_after>import gql from 'graphql-tag';
import { useRouter } from 'next/router';
import Head from 'next/head';
import { t } from 'ttag';
import { useQuery } from '@apollo/react-hooks';

import useCurrentUser from 'lib/useCurrentUser';
import * as FILTERS from 'constants/articleFilters';
import {
  ListPageCards,
  ArticleCard,
  ReplyItem,
  ListPageHeader,
} from 'components/ListPageDisplays';
import {
  Tools,
  Filters,
  ArticleStatusFilter,
  CategoryFilter,
  ReplyTypeFilter,
  TimeRange,
  SortInput,
  LoadMore,
} from 'components/ListPageControls';
import FeedDisplay from 'components/Subscribe/FeedDisplay';
import AppLayout from 'components/AppLayout';
import withData from 'lib/apollo';

const MAX_KEYWORD_LENGTH = 100;
const DEFAULT_ORDER = 'lastRequestedAt';

const LIST_ARTICLES = gql`
  query GetArticlesList(
    $filter: ListArticleFilter
    $orderBy: [ListArticleOrderBy]
    $after: String
  ) {
    ListArticles(filter: $filter, orderBy: $orderBy, after: $after, first: 25) {
      edges {
        node {
          id
          replyRequestCount
          createdAt
          text
          articleReplies(status: NORMAL) {
            reply {
              id
              ...ReplyItem
            }
            ...ReplyItemArticleReplyData
          }
          ...ArticleCard
        }
        cursor
      }
    }
  }
  ${ArticleCard.fragments.ArticleCard}
  ${ReplyItem.fragments.ReplyItem}
  ${ReplyItem.fragments.ReplyItemArticleReplyData}
`;

const LIST_STAT = gql`
  query GetArticlesListStat(
    $filter: ListArticleFilter
    $orderBy: [ListArticleOrderBy]
  ) {
    ListArticles(filter: $filter, orderBy: $orderBy, first: 25) {
      pageInfo {
        firstCursor
        lastCursor
      }
      totalCount
    }
  }
`;

/**
 * @param {object} urlQuery - URL query object
 * @returns {object} ListArticleFilter
 */
function urlQuery2Filter({
  filters,
  q,
  categoryIds,
  start,
  end,
  types,
  userId,
} = {}) {
  const filterObj = {};

  if (q) {
    filterObj.moreLikeThis = {
      like: q.slice(0, MAX_KEYWORD_LENGTH),
      minimumShouldMatch: '0',
    };
  }

  if (categoryIds) {
    filterObj.categoryIds = categoryIds.split(',');
  }

  const selectedFilters = typeof filters === 'string' ? filters.split(',') : [];
  selectedFilters.forEach(filter => {
    switch (filter) {
      case FILTERS.REPLIED_BY_ME:
        if (!userId) break;
        filterObj.articleRepliesFrom = {
          userId: userId,
          exists: true,
        };
        break;
      case FILTERS.NO_USEFUL_REPLY_YET:
        filterObj.hasArticleReplyWithMorePositiveFeedback = false;
        break;
      case FILTERS.ASKED_MANY_TIMES:
        filterObj.replyRequestCount = { GTE: 2 };
        break;
      case FILTERS.REPLIED_MANY_TIMES:
        filterObj.replyCount = { GTE: 3 };
        break;
      default:
    }
  });

  if (start) {
    filterObj.createdAt = { ...filterObj.createdAt, GTE: start };
  }
  if (end) {
    filterObj.createdAt = { ...filterObj.createdAt, LTE: end };
  }

  if (types) {
    filterObj.replyTypes = types.split(',');
  }

  // Return filterObj only when it is populated.
  if (!Object.keys(filterObj).length) {
    return undefined;
  }

  return filterObj;
}

function ArticleListPage() {
  const { query } = useRouter();
  const user = useCurrentUser();

  const listQueryVars = {
    filter: urlQuery2Filter({
      ...query,
      userId: user?.id,
    }),
    orderBy: [{ [query.orderBy || DEFAULT_ORDER]: 'DESC' }],
  };

  const {
    loading,
    fetchMore,
    data: listArticlesData,
    error: listArticlesError,
  } = useQuery(LIST_ARTICLES, {
    variables: listQueryVars,
    notifyOnNetworkStatusChange: true, // Make loading true on `fetchMore`
  });

  // Separate these stats query so that it will be cached by apollo-client and sends no network request
  // on page change, but still works when filter options are updated.
  //
  const { data: listStatData } = useQuery(LIST_STAT, {
    variables: listQueryVars,
  });

  // List data
  const articleEdges = listArticlesData?.ListArticles?.edges || [];
  const statsData = listStatData?.ListArticles || {};

  return (
    <AppLayout>
      <Head>
        <title>{t`Dubious Messages`}</title>
      </Head>
      <ListPageHeader title={t`Dubious Messages`}>
        <FeedDisplay listQueryVars={listQueryVars} />
      </ListPageHeader>

      <Tools>
        <TimeRange />
        <SortInput
          defaultOrderBy="lastRequestedAt"
          options={[
            { value: 'lastRequestedAt', label: t`Most recently asked` },
            { value: 'replyRequestCount', label: t`Most asked` },
            { value: 'lastRepliedAt', label: t`Most recently replied` },
          ]}
        />
      </Tools>

      <Filters>
        <ArticleStatusFilter />
        <ReplyTypeFilter />
        <CategoryFilter />
      </Filters>

      {loading && !articleEdges.length ? (
        t`Loading...`
      ) : listArticlesError ? (
        listArticlesError.toString()
      ) : (
        <>
          <ListPageCards>
            {articleEdges.map(({ node: article }) => (
              <ArticleCard key={article.id} article={article} query={query.q} />
            ))}
          </ListPageCards>

          <LoadMore
            edges={articleEdges}
            pageInfo={statsData?.pageInfo}
            loading={loading}
            onMoreRequest={args =>
              fetchMore({
                variables: args,
                updateQuery(prev, { fetchMoreResult }) {
                  if (!fetchMoreResult) return prev;
                  const newArticleData = fetchMoreResult?.ListArticles;
                  return {
                    ...prev,
                    ListArticles: {
                      ...newArticleData,
                      edges: [...articleEdges, ...newArticleData.edges],
                    },
                  };
                },
              })
            }
          />
        </>
      )}
    </AppLayout>
  );
}

export default withData(ArticleListPage);
<|endoftext|>


<commit_before>import React from 'react'
import { Link } from 'react-router'
import { prefixLink } from 'gatsby-helpers'
import Helmet from 'react-helmet'
import { config } from 'config'

const styles={
  link:{
    fontFamily:'Ubuntu',
    color:'black',
    textDecoration:'none'
  }
}

export default class Index extends React.Component {
  render() {
    return (

          Contacts

<Link  style={styles.link} to='mailto:holatchahl@gmail.com'>Email: holatchahl@gmail.com</Link>

<Link  style={styles.link} to='skype:aitops'>Skype: aitops</Link>

    )
  }
}
<commit_msg>Fix links contacts<commit_after>import React from 'react'
import { Link } from 'react-router'
import { prefixLink } from 'gatsby-helpers'
import Helmet from 'react-helmet'
import { config } from 'config'

const styles={
  link:{
    fontFamily:'Ubuntu',
    color:'black',
    textDecoration:'none'
  }
}

export default class Index extends React.Component {
  render() {
    return (

          Contacts

Email: holatchahl@gmail.com

Skype: aitops

    )
  }
}
<|endoftext|>


<commit_before>var View = require('./view');
var Graph = require('../models/graph');

module.exports = View.extend({

    model:Graph,

    initialize:function () {
        _.bindAll(this, 'render', 'showData');
    },

    afterRender:function () {
        this.$el.addClass('parcoords').attr('id', 'test');
        this.model.on('load', this.showData);
    },

    showData:function () {
        var nodes = this.model.get("nodes");
        var keys = _.difference(_.keys(nodes[0]), ['label', 'type', 'source', 'feature_id', 'nByi', '_pos']);

        var pc = d3.parcoords()('#test');

        pc.dimensions(keys)
            .data(nodes)
            .render()
            .color("#000")
            .alpha(0.3)
            .margin({ top:30, left:0, bottom:12, right:0 })
            .render()
            .reorderable()
            .brushable()
            .on('brush', this.model.filterNodes);

        pc.svg.selectAll(".parcoords-dimension")
            .on("click", toggle_dimension);

        function select(dimension) {
            pc.svg.selectAll(".parcoords-dimension")
                .filter(function (d) {
                    return d == dimension;
                })
                .classed('selected', true);
        }

        function unselect() {
            pc.svg.selectAll(".parcoords-dimension")
                .classed('selected', false);
        }

        function toggle_dimension(dimension) {

            var dim = pc.svg.selectAll(".parcoords-dimension.selected")
                .filter(function (d) {
                    return d == dimension;
                });

            if (dim[0].length > 0) {  //this was previously selected
                unselect();
                Backbone.Events.trigger('');
                console.log('previous');
            } else {    //this was not previously selected
                unselect();
                select(dimension);
                Backbone.Mediator.pub('dimension:select', dimension);
            }
        }

    }
});<commit_msg>throttled brush function<commit_after>var View = require('./view');
var Graph = require('../models/graph');

module.exports = View.extend({

    model:Graph,

    initialize:function () {
        _.bindAll(this, 'render', 'showData');
    },

    afterRender:function () {
        this.$el.addClass('parcoords').attr('id', 'test');
        this.model.on('load', this.showData);
    },

    showData:function () {
        var nodes = this.model.get("nodes");
        var keys = _.difference(_.keys(nodes[0]), ['label', 'type', 'source', 'feature_id', 'nByi', '_pos']);

        var pc = d3.parcoords()('#test');

        var _this = this;
        var brushFn = function(nodes) {
            _this.trigger("select-nodes", nodes);
        };

        pc.dimensions(keys)
            .data(nodes)
            .render()
            .color("#000")
            .alpha(0.3)
            .margin({ top:30, left:0, bottom:12, right:0 })
            .render()
            .reorderable()
            .brushable()
            .on('brush', _.throttle(brushFn, 500));

        pc.svg.selectAll(".parcoords-dimension")
            .on("click", toggle_dimension);

        function select(dimension) {
            pc.svg.selectAll(".parcoords-dimension")
                .filter(function (d) {
                    return d == dimension;
                })
                .classed('selected', true);
        }

        function unselect() {
            pc.svg.selectAll(".parcoords-dimension")
                .classed('selected', false);
        }

        function toggle_dimension(dimension) {

            var dim = pc.svg.selectAll(".parcoords-dimension.selected")
                .filter(function (d) {
                    return d == dimension;
                });

            if (dim[0].length > 0) {  //this was previously selected
                unselect();
                Backbone.Events.trigger('');
                console.log('previous');
            } else {    //this was not previously selected
                unselect();
                select(dimension);
                Backbone.Mediator.pub('dimension:select', dimension);
            }
        }

    }
});<|endoftext|>