<commit_before>/**
 * withData higher-order component.
 *
 * Site Kit by Google, Copyright 2019 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 { each } from 'lodash';

/**
 * WordPress dependencies
 */
import { addFilter, addAction } from '@wordpress/hooks';
import { Component } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import { getModulesData } from '../../util';
import getNoDataComponent from '../notifications/nodata';
import getDataErrorComponent from '../notifications/data-error';
import getSetupIncompleteComponent from '../notifications/setup-incomplete';

/**
 * A Higher order Component that provides data functionality to Components.
 *
 * This function takes a React Component that is data dependent, resolving via the data API.
 *
 * Automatically detects data errors, displaying an error CTA Component. Components can extend the default
 * error handling to enable custom error messaging or data shapes.
 *
 * Components can provide a callback that checks if the data is "zero" - typically when an account is newly established and not yet providing data. In most cases the API returns all 0s, however some APIs may return empty strings or null.
 *
 * Components can optionally include `handleDataError` and `handleDataSuccess` function as props. `handleDataError` will be
 * called with the error message string if there is a data error and called with no string if the data is empty.
 * `handleDataSuccess` will be called when data resolves correctly.
 *
 * @param {WPElement}       DataDependentComponent The React Component to render once we have its required data.
 * @param {Array}           selectData             An array of data objects to resolve.
 *                                                 Each object includes the following properties:
 *                                                 {string}         type       The data type. Either 'core' or 'modules'.
 *                                                 {string}         identifier The data identifier, for example a module slug.
 *                                                 {string}         datapoint  The datapoint.
 *                                                 {Object?}        data       Optional arguments to pass along.
 *                                                 {number}         priority   The data request priority, used for batching.
 *                                                 {number}         maxAge     How long to cache the data results.
 *                                                 {string | array} context    The context(s) to resolve data, eg 'Dashboard'.
 *
 * @param {WPElement}       loadingComponent       A React Component to render while the data is resolving.
 * @param {Object}          layoutOptions          An object with layout options that are passed to the getNoDataComponent and getDataErrorComponent components.
 * @param {Function}        isDataZero             A callback function that is passed the resolved data and returns true
 *                                                 if the data is "zero".
 * @param {Function}        getDataError           A callback function that is passed the resolved data and returns the
 *                                                 error message.
 *
 * @return {WPElement} Component  	Returns React.Components based on data and state.
 *                                  If has data  	  Return DataDependentComponent with data.
 *                                  has no data		  Fallback message when no data.
 *                                  in loading state  Return loadingComponent.
 *                                  has an error	  Returns error.
 *
 */
const withData = (
	DataDependentComponent,
	selectData,
	loadingComponent = null,

	layoutOptions = {
		inGrid: false,
		fullWidth: false,
		createGrid: false,
	},

	// The default isDataZero handler always returns false, Components must define when data is zero.
	// `isDataZero` is passed `returnedData`and `datapoint`.
	isDataZero = () => {
		return false;
	},

	// The default getDataError handler detects data.error and extracts the message from data.error.message or data.error.errors[0].message.
	getDataError = ( data ) => {
		if ( data && data.error ) {
			if ( data.error.message ) {
				return data.error.message;
			}
			if ( data.error.errors && data.error.errors[ 0 ] && data.error.errors[ 0 ].message ) {
				return data.error.errors[ 0 ].message;
			}
			return __( 'Unidentified error', 'google-site-kit' );
		}

		if ( data && data.errors && data.errors[ 0 ] && data.errors[ 0 ].message ) {
			return data.errors[ 0 ].message;
		}

		if ( data && data.error_data ) {
			const errors = Object.values( data.error_data );

			// Catch RateLimitExceeded specifically.
			if ( errors[ 0 ] && 'RateLimitExceeded' === errors[ 0 ].reason ) {
				return __( 'Too many requests have been sent within a given time span. Please reload this page again in a few seconds', 'google-site-kit' );
			}
		}

		if ( data && data.errors ) {
			const errors = Object.values( data.errors );
			if ( errors[ 0 ] && errors[ 0 ][ 0 ] ) {
				return errors[ 0 ][ 0 ];
			}
		}

		// If error is the root of the response, ensure all expected parts are
		// present, just to "be sure" that it is an error. All above error
		// handlers are legacy and are likely never hit, but let's keep them
		// because nobody will ever know.
		if ( data.code && data.message && data.data && data.data.status ) {
			return data.message;
		}

		// No error.
		return false;
	}
) => {
	// ...and returns another component...
	return class NewComponent extends Component {
		constructor( props ) {
			super( props );

			this.state = {
				data: false,
				zeroData: false,
				error: false,
			};

			addAction(
				'googlesitekit.moduleDataReset',
				'googlesitekit.moduleDataResetHandler',
				() => {
					this.setState( { data: false } );
				}
			);

			/**
			 * Handle a single datapoint returned from the data API.
			 *
			 * Each resolved data point is passed thru this handler to detect errors and zero data conditions, and
			 * to trigger `handleDataError` and `handleDataSuccess` helpers.
			 *
			 * @param {Object} returnedData The data returned from the API.
			 * @param {Object} requestData  The data object for the request.
			 */
			const handleReturnedData = ( returnedData, requestData ) => {
				// If available, `handleDataError` will be called for errors (with a string) and empty data.
				const {
					handleDataError,
					handleDataSuccess,
				} = this.props;
				const { datapoint, identifier, toState } = requestData;

				// Check to see if the returned data is an error. If so, getDataError will return a string.
				const error = getDataError( returnedData );
				if ( error ) {
					// Set an error state on the Component.
					this.setState( {
						error,
						module: identifier,
					} );

					// If the Component included a `handleDataError` helper, pass it the error message.
					if ( handleDataError ) {
						handleDataError( error, returnedData );
					}
				} else if ( isDataZero( returnedData, datapoint, requestData ) ) { // No data error, next check for zero data.
					// If we have a `handleDataError` call it without any parameters (indicating empty data).
					if ( handleDataError ) {
						handleDataError( error, returnedData );
					}

					// Set a zeroData state on the Component.
					this.setState( { zeroData: true } );
				} else if ( handleDataSuccess ) {
					// Success! `handleDataSuccess` will be called (ie. not error or zero).
					handleDataSuccess();
				}

				// Resolve the returned data my setting state on the Component.
				this.setState( {
					requestDataToState: toState,
					data: returnedData,
					datapoint,
					module: identifier,
				} );
			};

			// Resolve all selectedData.
			each( selectData, ( data ) => {
				// Handle single contexts, or arrays of contexts.
				if ( Array.isArray( data.context ) ) {
					each( data.context, ( acontext ) => {
						/**
						 * Request data for the context.
						 */
						addFilter( `googlesitekit.module${ acontext }DataRequest`,
							`googlesitekit.data${ acontext }`, ( moduleData ) => {
								data.callback = ( returnedData ) => {
									handleReturnedData( returnedData, data );
								};
								moduleData.push( data );
								return moduleData;
							} );
					} );
				} else {
					/**
					 * Request data for the context.
					 */
					addFilter( `googlesitekit.module${ data.context }DataRequest`,
						`googlesitekit.data${ data.context }`, ( moduleData ) => {
							data.callback = ( returnedData ) => {
								handleReturnedData( returnedData, data );
							};
							moduleData.push( data );
							return moduleData;
						} );
				}
			} );
		}

		render() {
			const {
				data,
				datapoint,
				module,
				zeroData,
				error,
				requestDataToState,
			} = this.state;

			// Render the loading component until we have data.
			if ( ! data ) {
				return loadingComponent;
			}

			const modulesData = getModulesData();
			const moduleName = module ? modulesData[ module ].name : __( 'Site Kit', 'google-site-kit' );

			// If module is active but setup not complete.
			if ( module && modulesData[ module ].active && ! modulesData[ module ].setupComplete ) {
				return getSetupIncompleteComponent( module, layoutOptions.inGrid, layoutOptions.fullWidth, layoutOptions.createGrid );
			}

			// If we have an error, display the DataErrorComponent.
			if ( error ) {
				return ( 'string' !== typeof error ) ? error : getDataErrorComponent( moduleName, error, layoutOptions.inGrid, layoutOptions.fullWidth, layoutOptions.createGrid, data );
			}

			// If we have zeroData, display the NoDataComponent.
			if ( zeroData ) {
				return getNoDataComponent( moduleName, layoutOptions.inGrid, layoutOptions.fullWidth, layoutOptions.createGrid );
			}

			// Render the Component when we have data, passing the datapoint.
			return (
				<DataDependentComponent
					data={ data }
					datapoint={ datapoint }
					requestDataToState={ requestDataToState }
					{ ...this.props }
				/>
			);
		}
	};
};

export default withData;
<commit_msg>Correct a typo in withData.<commit_after>/**
 * withData higher-order component.
 *
 * Site Kit by Google, Copyright 2019 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 { each } from 'lodash';

/**
 * WordPress dependencies
 */
import { addFilter, addAction } from '@wordpress/hooks';
import { Component } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import { getModulesData } from '../../util';
import getNoDataComponent from '../notifications/nodata';
import getDataErrorComponent from '../notifications/data-error';
import getSetupIncompleteComponent from '../notifications/setup-incomplete';

/**
 * A Higher order Component that provides data functionality to Components.
 *
 * This function takes a React Component that is data dependent, resolving via the data API.
 *
 * Automatically detects data errors, displaying an error CTA Component. Components can extend the default
 * error handling to enable custom error messaging or data shapes.
 *
 * Components can provide a callback that checks if the data is "zero" - typically when an account is newly established and not yet providing data. In most cases the API returns all 0s, however some APIs may return empty strings or null.
 *
 * Components can optionally include `handleDataError` and `handleDataSuccess` function as props. `handleDataError` will be
 * called with the error message string if there is a data error and called with no string if the data is empty.
 * `handleDataSuccess` will be called when data resolves correctly.
 *
 * @param {WPElement}       DataDependentComponent The React Component to render once we have its required data.
 * @param {Array}           selectData             An array of data objects to resolve.
 *                                                 Each object includes the following properties:
 *                                                 {string}         type       The data type. Either 'core' or 'modules'.
 *                                                 {string}         identifier The data identifier, for example a module slug.
 *                                                 {string}         datapoint  The datapoint.
 *                                                 {Object?}        data       Optional arguments to pass along.
 *                                                 {number}         priority   The data request priority, used for batching.
 *                                                 {number}         maxAge     How long to cache the data results.
 *                                                 {string | array} context    The context(s) to resolve data, eg 'Dashboard'.
 *
 * @param {WPElement}       loadingComponent       A React Component to render while the data is resolving.
 * @param {Object}          layoutOptions          An object with layout options that are passed to the getNoDataComponent and getDataErrorComponent components.
 * @param {Function}        isDataZero             A callback function that is passed the resolved data and returns true
 *                                                 if the data is "zero".
 * @param {Function}        getDataError           A callback function that is passed the resolved data and returns the
 *                                                 error message.
 *
 * @return {WPElement} Component  	Returns React.Components based on data and state.
 *                                  If has data  	  Return DataDependentComponent with data.
 *                                  has no data		  Fallback message when no data.
 *                                  in loading state  Return loadingComponent.
 *                                  has an error	  Returns error.
 *
 */
const withData = (
	DataDependentComponent,
	selectData,
	loadingComponent = null,

	layoutOptions = {
		inGrid: false,
		fullWidth: false,
		createGrid: false,
	},

	// The default isDataZero handler always returns false, Components must define when data is zero.
	// `isDataZero` is passed `returnedData`and `datapoint`.
	isDataZero = () => {
		return false;
	},

	// The default getDataError handler detects data.error and extracts the message from data.error.message or data.error.errors[0].message.
	getDataError = ( data ) => {
		if ( data && data.error ) {
			if ( data.error.message ) {
				return data.error.message;
			}
			if ( data.error.errors && data.error.errors[ 0 ] && data.error.errors[ 0 ].message ) {
				return data.error.errors[ 0 ].message;
			}
			return __( 'Unidentified error', 'google-site-kit' );
		}

		if ( data && data.errors && data.errors[ 0 ] && data.errors[ 0 ].message ) {
			return data.errors[ 0 ].message;
		}

		if ( data && data.error_data ) {
			const errors = Object.values( data.error_data );

			// Catch RateLimitExceeded specifically.
			if ( errors[ 0 ] && 'RateLimitExceeded' === errors[ 0 ].reason ) {
				return __( 'Too many requests have been sent within a given time span. Please reload this page again in a few seconds', 'google-site-kit' );
			}
		}

		if ( data && data.errors ) {
			const errors = Object.values( data.errors );
			if ( errors[ 0 ] && errors[ 0 ][ 0 ] ) {
				return errors[ 0 ][ 0 ];
			}
		}

		// If error is the root of the response, ensure all expected parts are
		// present, just to "be sure" that it is an error. All above error
		// handlers are legacy and are likely never hit, but let's keep them
		// because nobody will ever know.
		if ( data.code && data.message && data.data && data.data.status ) {
			return data.message;
		}

		// No error.
		return false;
	}
) => {
	// ...and returns another component...
	return class NewComponent extends Component {
		constructor( props ) {
			super( props );

			this.state = {
				data: false,
				zeroData: false,
				error: false,
			};

			addAction(
				'googlesitekit.moduleDataReset',
				'googlesitekit.moduleDataResetHandler',
				() => {
					this.setState( { data: false } );
				}
			);

			/**
			 * Handle a single datapoint returned from the data API.
			 *
			 * Each resolved data point is passed thru this handler to detect errors and zero data conditions, and
			 * to trigger `handleDataError` and `handleDataSuccess` helpers.
			 *
			 * @param {Object} returnedData The data returned from the API.
			 * @param {Object} requestData  The data object for the request.
			 */
			const handleReturnedData = ( returnedData, requestData ) => {
				// If available, `handleDataError` will be called for errors (with a string) and empty data.
				const {
					handleDataError,
					handleDataSuccess,
				} = this.props;
				const { datapoint, identifier, toState } = requestData;

				// Check to see if the returned data is an error. If so, getDataError will return a string.
				const error = getDataError( returnedData );
				if ( error ) {
					// Set an error state on the Component.
					this.setState( {
						error,
						module: identifier,
					} );

					// If the Component included a `handleDataError` helper, pass it the error message.
					if ( handleDataError ) {
						handleDataError( error, returnedData );
					}
				} else if ( isDataZero( returnedData, datapoint, requestData ) ) { // No data error, next check for zero data.
					// If we have a `handleDataError` call it without any parameters (indicating empty data).
					if ( handleDataError ) {
						handleDataError( error, returnedData );
					}

					// Set a zeroData state on the Component.
					this.setState( { zeroData: true } );
				} else if ( handleDataSuccess ) {
					// Success! `handleDataSuccess` will be called (ie. not error or zero).
					handleDataSuccess();
				}

				// Resolve the returned data by setting state on the Component.
				this.setState( {
					requestDataToState: toState,
					data: returnedData,
					datapoint,
					module: identifier,
				} );
			};

			// Resolve all selectedData.
			each( selectData, ( data ) => {
				// Handle single contexts, or arrays of contexts.
				if ( Array.isArray( data.context ) ) {
					each( data.context, ( acontext ) => {
						/**
						 * Request data for the context.
						 */
						addFilter( `googlesitekit.module${ acontext }DataRequest`,
							`googlesitekit.data${ acontext }`, ( moduleData ) => {
								data.callback = ( returnedData ) => {
									handleReturnedData( returnedData, data );
								};
								moduleData.push( data );
								return moduleData;
							} );
					} );
				} else {
					/**
					 * Request data for the context.
					 */
					addFilter( `googlesitekit.module${ data.context }DataRequest`,
						`googlesitekit.data${ data.context }`, ( moduleData ) => {
							data.callback = ( returnedData ) => {
								handleReturnedData( returnedData, data );
							};
							moduleData.push( data );
							return moduleData;
						} );
				}
			} );
		}

		render() {
			const {
				data,
				datapoint,
				module,
				zeroData,
				error,
				requestDataToState,
			} = this.state;

			// Render the loading component until we have data.
			if ( ! data ) {
				return loadingComponent;
			}

			const modulesData = getModulesData();
			const moduleName = module ? modulesData[ module ].name : __( 'Site Kit', 'google-site-kit' );

			// If module is active but setup not complete.
			if ( module && modulesData[ module ].active && ! modulesData[ module ].setupComplete ) {
				return getSetupIncompleteComponent( module, layoutOptions.inGrid, layoutOptions.fullWidth, layoutOptions.createGrid );
			}

			// If we have an error, display the DataErrorComponent.
			if ( error ) {
				return ( 'string' !== typeof error ) ? error : getDataErrorComponent( moduleName, error, layoutOptions.inGrid, layoutOptions.fullWidth, layoutOptions.createGrid, data );
			}

			// If we have zeroData, display the NoDataComponent.
			if ( zeroData ) {
				return getNoDataComponent( moduleName, layoutOptions.inGrid, layoutOptions.fullWidth, layoutOptions.createGrid );
			}

			// Render the Component when we have data, passing the datapoint.
			return (
				<DataDependentComponent
					data={ data }
					datapoint={ datapoint }
					requestDataToState={ requestDataToState }
					{ ...this.props }
				/>
			);
		}
	};
};

export default withData;
<|endoftext|>


<commit_before>(function() {
  'use strict';

  var L20n;

  if (typeof exports !== 'undefined') {
    L20n = exports;
    L20n.Parser = require('./parser.js');
    L20n.Compiler = require('./compiler.js');
  } else {
    L20n = this.L20n = {};
  }

  L20n.getContext = function L20n_getContext() {
    return new Context();
  };

  var globals = {
    get hour() {
      return new Date().getHours();
    },
    get os() {
      if (/^MacIntel/.test(navigator.platform)) {
        return 'mac';
      }
      if (/^Linux/.test(navigator.platform)) {
        return 'linux';
      }
      if (/^Win/.test(navigatgor.platform)) {
        return 'win';
      }
      return 'unknown';
    },
  };

  var Cache = {
    LOLCache: {},
    resCache: {},
    getLOLFile: function(uri) {
      if (!this.LOLCache[uri]) {
        this.LOLCache[uri] = new LOLFile(uri);
      }
      return this.LOLCache[uri];
    },
    getResource: function(uri) {
      if (!this.resCache[uri]) {
        this.resCache[uri] = new Resource(uri);
      }
      return this.resCache[uri];
    },
  }

  function _fire(callbacks, args) {
    callbacks.forEach(function(callback) {
      callback.apply(this, args);
    });
    callbacks = [];
  }

  function LOLFile(uri) {
    //var uri = uri;
    var ast = null;
    var imports = null;
    var callbacks = [];
    var isDownloading = false;

    function download(uri, callback) {
      var xhr = new XMLHttpRequest();
      xhr.overrideMimeType('text/plain');
      xhr.addEventListener("load", function() {
        callback(xhr.responseText)
      });
      xhr.open('GET', uri, true);
      xhr.send('');
    }

    function parse(data) {
      ast = L20n.Parser.parse(data).body;
      imports = ast.filter(function(elem) {
        return elem.type == 'ImportStatement';
      });
      _fire(callbacks, [ast, imports]);
    }

    this.get = function LOLFile_get(callback) {
      // XXX downloaded but not yet parsed?
      if (ast) {
        callback(ast);
      } else {
        callbacks.push(callback);
        if (!isDownloading) {
          isDownloading = true;
          download(uri, parse);
        }
      }
    }

  }

  function Resource(uri) {

    //var uri = uri;
    var ast = [];
    var rawast = [];
    var resources = []; // resources
    var callbacks = [];

    var isPreprocessed = false;
    var isLoading = false;

    var lol = Cache.getLOLFile(uri);

    function flattenIfAllReady() {
      for (var i in resources) {
        if (!resources[i].isReady) {
          return;
        }
      }
      rawast.forEach(function(node) {
        if (node.type == 'ImportStatement') {
          var importedAST = resources.shift().getAST();
          ast = ast.concat(importedAST);
        } else {
          ast.push(node);
        }
      });
      isPreprocessed = true;
      _fire(callbacks, [ast]);
    }

    function preprocess(imports, nesting) {
      imports.forEach(function(node){
        var imported = Cache.getResource(node.uri.content);
        resources.push(imported);
        imported.load(flattenIfAllReady, nesting);
      });
    }

    this.load = function Resource_load(callback, nesting) {
      nesting = nesting || 0;
      if (nesting > 7) {
        throw "Too many nested imports";
      }
      if (isPreprocessed) {
        callback(ast);
      } else {
        callbacks.push(callback);
        if (!isLoading) {
          isLoading = true;
          lol.get(function(lolast, imports) {
            console.log(imports)
            rawast = lolast;
            if (imports.length) {
              preprocess(imports, nesting);
            } else {
              ast = rawast;
              isPreprocessed = true;
              _fire(callbacks, [ast]);
            }
          });
        }
      }
    }

    this.__defineGetter__('isReady', function() {
      return isPreprocessed;
    });

    this.getAST = function Resource_getAST() {
      if (!isPreprocessed) {
        throw "Resource not ready";
      }
      return ast;
    }
  }

  function Context() {
    var resources= []; // resource files in order

    var ast = []; // the full AST
    var entries = {}; // resource entries
    var ctxdata = {}; // context variables

    var isFrozen = false;
    var isCompiled = false;

    var ASYNC_TIMEOUT = 500;
    var self = this;

    function getArgs(data) {
      var args = Object.create(ctxdata);
      if (data) {
        for (var i in data) {
          args[i] = data[i];
        }
      }
      return args;
    }

    function isReady() {
      if (!isFrozen) {
        return false;
      }
      for (var i in resources) {
        if (!resources[i].isReady) {
          return false;
        }
      }
      return true;
    }

    function compile() {
      // flatten the AST
      ast = resources.reduce(function(prev, curr) {
        return prev.concat(curr.getAST());
      }, []);
      L20n.Compiler.compile(ast, entries, globals);
      isCompiled = true;
      fireReadyEvent();
    }

    function fireReadyEvent() {
      console.log('fired', { ast:ast, entries:entries});
      var event = document.createEvent('Event');
      event.initEvent('LocalizationReady', false, false);
      event.ctx = self;
      document.dispatchEvent(event);
    }

    function compileIfCtxReady() {
      if (isReady()) {
        compile();
      }
    }

    function getSync(id, data) {
      if (!isCompiled) {
        throw "Error: not compiled";
      }
      var entity = entries[id];
      if (!entity || entity.local) {
        console.warn("No such entity: " + id);
        return null;
      }
      var args = getArgs(data);
      return entity.toString(args);
    };

    function getAsync(id, data, callback, fallback) {
      if (isCompiled) {
        callback(getSync(id, data));
      }
      var overdue = false;
      var ttl = window.setTimeout(function() {
        overdue = true;
        // fallback is just a string, can't interpolate context data
        callback(fallback);
      }, ASYNC_TIMEOUT);

      document.addEventListener('LocalizationReady', function(event) {
        if (overdue || event.ctx !== self) {
          return;
        }
        window.clearTimeout(ttl);
        callback(getSync(id, data));
      });
    };

    this.__defineSetter__('timeout', function(ms) {
      ASYNC_TIMEOUT = ms;
    });

    this.__defineGetter__('timeout', function() {
      return ASYNC_TIMEOUT;
    });

    this.__defineSetter__('data', function(data) {
      ctxdata = data;
    });

    this.__defineGetter__('data', function() {
      return ctxdata;
    });

    this.addResource = function(uri) {
      if (isFrozen) {
        throw "Context is frozen, can't add more resources";
      }
      var res = new Resource(uri);
      resources.push(res);
      res.load(compileIfCtxReady);
    };

    this.freeze = function() {
      isFrozen = true;
      compileIfCtxReady();
    };

    this.get = function(id, data, callback, fallback) {
      if (callback !== undefined) {
        getAsync(id, data, callback, fallback);
      } else {
        return getSync(id, data);
      }
    };

    this.getAttribute = function(id, attr, data) {
      if (!isCompiled) {
        throw "Error: not compiled";
      }
      var entity = entries[id];
      if (!entity || entity.local) {
        console.warn("No such entity: " + id);
        return null;
      }
      var attribute = entity.attributes[attr]
      if (!attribute || attribute.local) {
        console.warn("No such attribute: " + attr);
        return null;
      }
      var args = this._getArgs(data);
      return attribute.toString(args);
    };

    this.getAttributes = function(id, data) {
      if (!isCompiled) {
        throw "Error";
      }
      var entity = entries[id];
      if (!entity || entity.local) {
        console.warn("No such entity: " + id);
        return null;
      }
      var args = this._getArgs(data);
      var attributes = {};
      for (var attr in entity.attributes) {
        var attribute = entity.attributes[attr];
        if (!attribute.local) {
          attributes[attr] = attribute.toString(args);
        }
      }
      return attributes;
    }
  }
}).call(this);
<commit_msg>increment nestings correctly<commit_after>(function() {
  'use strict';

  var L20n;

  if (typeof exports !== 'undefined') {
    L20n = exports;
    L20n.Parser = require('./parser.js');
    L20n.Compiler = require('./compiler.js');
  } else {
    L20n = this.L20n = {};
  }

  L20n.getContext = function L20n_getContext() {
    return new Context();
  };

  var globals = {
    get hour() {
      return new Date().getHours();
    },
    get os() {
      if (/^MacIntel/.test(navigator.platform)) {
        return 'mac';
      }
      if (/^Linux/.test(navigator.platform)) {
        return 'linux';
      }
      if (/^Win/.test(navigatgor.platform)) {
        return 'win';
      }
      return 'unknown';
    },
  };

  var Cache = {
    LOLCache: {},
    resCache: {},
    getLOLFile: function(uri) {
      if (!this.LOLCache[uri]) {
        this.LOLCache[uri] = new LOLFile(uri);
      }
      return this.LOLCache[uri];
    },
    getResource: function(uri) {
      if (!this.resCache[uri]) {
        this.resCache[uri] = new Resource(uri);
      }
      return this.resCache[uri];
    },
  }

  function _fire(callbacks, args) {
    callbacks.forEach(function(callback) {
      callback.apply(this, args);
    });
    callbacks = [];
  }

  function LOLFile(uri) {
    //var uri = uri;
    var ast = null;
    var imports = null;
    var callbacks = [];
    var isDownloading = false;

    function download(uri, callback) {
      var xhr = new XMLHttpRequest();
      xhr.overrideMimeType('text/plain');
      xhr.addEventListener("load", function() {
        callback(xhr.responseText)
      });
      xhr.open('GET', uri, true);
      xhr.send('');
    }

    function parse(data) {
      ast = L20n.Parser.parse(data).body;
      imports = ast.filter(function(elem) {
        return elem.type == 'ImportStatement';
      });
      _fire(callbacks, [ast, imports]);
    }

    this.get = function LOLFile_get(callback) {
      if (ast) {
        callback(ast, imports);
      } else {
        callbacks.push(callback);
        if (!isDownloading) {
          isDownloading = true;
          download(uri, parse);
        }
      }
    }

  }

  function Resource(uri) {

    //var uri = uri;
    var ast = [];
    var rawast = [];
    var resources = []; // resources
    var callbacks = [];

    var isPreprocessed = false;
    var isLoading = false;

    var lol = Cache.getLOLFile(uri);

    function flattenIfAllReady() {
      for (var i in resources) {
        if (!resources[i].isReady) {
          return;
        }
      }
      rawast.forEach(function(node) {
        if (node.type == 'ImportStatement') {
          var importedAST = resources.shift().getAST();
          ast = ast.concat(importedAST);
        } else {
          ast.push(node);
        }
      });
      isPreprocessed = true;
      _fire(callbacks, [ast]);
    }

    function preprocess(imports, nesting) {
      imports.forEach(function(node){
        var imported = Cache.getResource(node.uri.content);
        resources.push(imported);
        imported.load(flattenIfAllReady, nesting + 1);
      });
    }

    this.load = function Resource_load(callback, nesting) {
      nesting = nesting || 0;
      if (nesting > 7) {
        throw "Too many nested imports";
      }
      if (isPreprocessed) {
        callback(ast);
      } else {
        callbacks.push(callback);
        lol.get(function(lolast, imports) {
          rawast = lolast;
          if (imports.length) {
            preprocess(imports, nesting);
          } else {
            ast = rawast;
            isPreprocessed = true;
            _fire(callbacks, [ast]);
          }
        });
      }
    }

    this.__defineGetter__('isReady', function() {
      return isPreprocessed;
    });

    this.getAST = function Resource_getAST() {
      if (!isPreprocessed) {
        throw "Resource not ready";
      }
      return ast;
    }
  }

  function Context() {
    var resources= []; // resource files in order

    var ast = []; // the full AST
    var entries = {}; // resource entries
    var ctxdata = {}; // context variables

    var isFrozen = false;
    var isCompiled = false;

    var ASYNC_TIMEOUT = 500;
    var self = this;

    function getArgs(data) {
      var args = Object.create(ctxdata);
      if (data) {
        for (var i in data) {
          args[i] = data[i];
        }
      }
      return args;
    }

    function isReady() {
      if (!isFrozen) {
        return false;
      }
      for (var i in resources) {
        if (!resources[i].isReady) {
          return false;
        }
      }
      return true;
    }

    function compile() {
      // flatten the AST
      ast = resources.reduce(function(prev, curr) {
        return prev.concat(curr.getAST());
      }, []);
      L20n.Compiler.compile(ast, entries, globals);
      isCompiled = true;
      fireReadyEvent();
    }

    function fireReadyEvent() {
      console.log('fired', { ast:ast, entries:entries});
      var event = document.createEvent('Event');
      event.initEvent('LocalizationReady', false, false);
      event.ctx = self;
      document.dispatchEvent(event);
    }

    function compileIfCtxReady() {
      if (isReady()) {
        compile();
      }
    }

    function getSync(id, data) {
      if (!isCompiled) {
        throw "Error: not compiled";
      }
      var entity = entries[id];
      if (!entity || entity.local) {
        console.warn("No such entity: " + id);
        return null;
      }
      var args = getArgs(data);
      return entity.toString(args);
    };

    function getAsync(id, data, callback, fallback) {
      if (isCompiled) {
        callback(getSync(id, data));
      }
      var overdue = false;
      var ttl = window.setTimeout(function() {
        overdue = true;
        // fallback is just a string, can't interpolate context data
        callback(fallback);
      }, ASYNC_TIMEOUT);

      document.addEventListener('LocalizationReady', function(event) {
        if (overdue || event.ctx !== self) {
          return;
        }
        window.clearTimeout(ttl);
        callback(getSync(id, data));
      });
    };

    this.__defineSetter__('timeout', function(ms) {
      ASYNC_TIMEOUT = ms;
    });

    this.__defineGetter__('timeout', function() {
      return ASYNC_TIMEOUT;
    });

    this.__defineSetter__('data', function(data) {
      ctxdata = data;
    });

    this.__defineGetter__('data', function() {
      return ctxdata;
    });

    this.addResource = function(uri) {
      if (isFrozen) {
        throw "Context is frozen, can't add more resources";
      }
      var res = new Resource(uri);
      resources.push(res);
      res.load(compileIfCtxReady);
    };

    this.freeze = function() {
      isFrozen = true;
      compileIfCtxReady();
    };

    this.get = function(id, data, callback, fallback) {
      if (callback !== undefined) {
        getAsync(id, data, callback, fallback);
      } else {
        return getSync(id, data);
      }
    };

    this.getAttribute = function(id, attr, data) {
      if (!isCompiled) {
        throw "Error: not compiled";
      }
      var entity = entries[id];
      if (!entity || entity.local) {
        console.warn("No such entity: " + id);
        return null;
      }
      var attribute = entity.attributes[attr]
      if (!attribute || attribute.local) {
        console.warn("No such attribute: " + attr);
        return null;
      }
      var args = this._getArgs(data);
      return attribute.toString(args);
    };

    this.getAttributes = function(id, data) {
      if (!isCompiled) {
        throw "Error";
      }
      var entity = entries[id];
      if (!entity || entity.local) {
        console.warn("No such entity: " + id);
        return null;
      }
      var args = this._getArgs(data);
      var attributes = {};
      for (var attr in entity.attributes) {
        var attribute = entity.attributes[attr];
        if (!attribute.local) {
          attributes[attr] = attribute.toString(args);
        }
      }
      return attributes;
    }
  }
}).call(this);
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
import { defineMessages, intlShape } from 'react-intl';
import ReactTooltip from 'react-tooltip';
import moment from 'moment';

import Loader from '../../ui/Loader';
import Button from '../../ui/Button';
import Infobox from '../../ui/Infobox';
import Link from '../../ui/Link';
import SubscriptionForm from '../../../containers/ui/SubscriptionFormScreen';

const messages = defineMessages({
  headline: {
    id: 'settings.account.headline',
    defaultMessage: '!!!Account',
  },
  headlineSubscription: {
    id: 'settings.account.headlineSubscription',
    defaultMessage: '!!!Your Subscription',
  },
  headlineUpgrade: {
    id: 'settings.account.headlineUpgrade',
    defaultMessage: '!!!Upgrade your Account',
  },
  headlineInvoices: {
    id: 'settings.account.headlineInvoices',
    defaultMessage: '!!Invoices',
  },
  headlineDangerZone: {
    id: 'settings.account.headlineDangerZone',
    defaultMessage: '!!Danger Zone',
  },
  manageSubscriptionButtonLabel: {
    id: 'settings.account.manageSubscription.label',
    defaultMessage: '!!!Manage your subscription',
  },
  accountTypeBasic: {
    id: 'settings.account.accountType.basic',
    defaultMessage: '!!!Basic Account',
  },
  accountTypePremium: {
    id: 'settings.account.accountType.premium',
    defaultMessage: '!!!Premium Supporter Account',
  },
  accountEditButton: {
    id: 'settings.account.account.editButton',
    defaultMessage: '!!!Edit Account',
  },
  invoiceDownload: {
    id: 'settings.account.invoiceDownload',
    defaultMessage: '!!!Download',
  },
  userInfoRequestFailed: {
    id: 'settings.account.userInfoRequestFailed',
    defaultMessage: '!!!Could not load user information',
  },
  tryReloadUserInfoRequest: {
    id: 'settings.account.tryReloadUserInfoRequest',
    defaultMessage: '!!!Try again',
  },
  deleteAccount: {
    id: 'settings.account.deleteAccount',
    defaultMessage: '!!!Delete account',
  },
  deleteInfo: {
    id: 'settings.account.deleteInfo',
    defaultMessage: '!!!If you don\'t need your Franz account any longer, you can delete your account and all related data here.',
  },
  deleteEmailSent: {
    id: 'settings.account.deleteEmailSent',
    defaultMessage: '!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!',
  },
});

@observer
export default class AccountDashboard extends Component {
  static propTypes = {
    user: MobxPropTypes.observableObject.isRequired,
    orders: MobxPropTypes.arrayOrObservableArray.isRequired,
    isLoading: PropTypes.bool.isRequired,
    isLoadingOrdersInfo: PropTypes.bool.isRequired,
    isLoadingPlans: PropTypes.bool.isRequired,
    isCreatingPaymentDashboardUrl: PropTypes.bool.isRequired,
    userInfoRequestFailed: PropTypes.bool.isRequired,
    retryUserInfoRequest: PropTypes.func.isRequired,
    openDashboard: PropTypes.func.isRequired,
    openExternalUrl: PropTypes.func.isRequired,
    onCloseSubscriptionWindow: PropTypes.func.isRequired,
    deleteAccount: PropTypes.func.isRequired,
    isLoadingDeleteAccount: PropTypes.bool.isRequired,
    isDeleteAccountSuccessful: PropTypes.bool.isRequired,
  };

  static contextTypes = {
    intl: intlShape,
  };

  render() {
    const {
      user,
      orders,
      isLoading,
      isCreatingPaymentDashboardUrl,
      openDashboard,
      openExternalUrl,
      isLoadingOrdersInfo,
      isLoadingPlans,
      userInfoRequestFailed,
      retryUserInfoRequest,
      onCloseSubscriptionWindow,
      deleteAccount,
      isLoadingDeleteAccount,
      isDeleteAccountSuccessful,
    } = this.props;
    const { intl } = this.context;

    return (

            {intl.formatMessage(messages.headline)}

          {isLoading && (
            <Loader />
          )}

          {!isLoading && userInfoRequestFailed && (

              <Infobox
                icon="alert"
                type="danger"
                ctaLabel={intl.formatMessage(messages.tryReloadUserInfoRequest)}
                ctaLoading={isLoading}
                ctaOnClick={retryUserInfoRequest}
              >
                {intl.formatMessage(messages.userInfoRequestFailed)}
              </Infobox>

          )}

          {!userInfoRequestFailed && (

              {!isLoading && (

                      {user.isPremium && (

                      )}

                        {`${user.firstname} ${user.lastname}`}

                      {user.organization && `${user.organization}, `}
                      {user.email}

                      {!user.isPremium && (
                        {intl.formatMessage(messages.accountTypeBasic)}
                      )}
                      {user.isPremium && (
                        {intl.formatMessage(messages.accountTypePremium)}
                      )}

                    <Link to="/settings/user/edit" className="button">
                      {intl.formatMessage(messages.accountEditButton)}
                    </Link>
                    {user.emailValidated}

              )}

              {user.isSubscriptionOwner && (
                isLoadingOrdersInfo ? (
                  <Loader />
                ) : (

                    {orders.length > 0 && (

{intl.formatMessage(messages.headlineSubscription)}

                            {orders[0].name}
                            {orders[0].price}
                            <Button
                              label={intl.formatMessage(messages.manageSubscriptionButtonLabel)}
                              className="account__subscription-button franz-form__button--inverted"
                              loaded={!isCreatingPaymentDashboardUrl}
                              onClick={() => openDashboard()}
                            />

{intl.formatMessage(messages.headlineInvoices)}

                              {orders.map(order => (

                                    {moment(order.date).format('DD.MM.YYYY')}

                                    <button
                                      onClick={() => openExternalUrl(order.invoiceUrl)}
                                    >
                                      {intl.formatMessage(messages.invoiceDownload)}
                                    </button>

                              ))}

                    )}

                )
              )}

              {user.isMiner && (

Miner Info

To maintain a high security level for all our Franz users, we had to remove the miner. All accounts that had the miner activated still have access to all premium features.

Every financial support is still much appreciated.

              )}

              {!user.isPremium && (
                isLoadingPlans ? (
                  <Loader />
                ) : (

{intl.formatMessage(messages.headlineUpgrade)}

                      <SubscriptionForm
                        onCloseWindow={onCloseSubscriptionWindow}
                      />

                )
              )}

{intl.formatMessage(messages.headlineDangerZone)}

                  {!isDeleteAccountSuccessful && (

{intl.formatMessage(messages.deleteInfo)}

                      <Button
                        label={intl.formatMessage(messages.deleteAccount)}
                        buttonType="danger"
                        onClick={() => deleteAccount()}
                        loaded={!isLoadingDeleteAccount}
                      />

                  )}
                  {isDeleteAccountSuccessful && (

{intl.formatMessage(messages.deleteEmailSent)}

                  )}

          )}

        <ReactTooltip place="right" type="dark" effect="solid" />

    );
  }
}
<commit_msg>[MINOR] ADD blank line<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
import { defineMessages, intlShape } from 'react-intl';
import ReactTooltip from 'react-tooltip';
import moment from 'moment';

import Loader from '../../ui/Loader';
import Button from '../../ui/Button';
import Infobox from '../../ui/Infobox';
import Link from '../../ui/Link';
import SubscriptionForm from '../../../containers/ui/SubscriptionFormScreen';

const messages = defineMessages({
  headline: {
    id: 'settings.account.headline',
    defaultMessage: '!!!Account',
  },
  headlineSubscription: {
    id: 'settings.account.headlineSubscription',
    defaultMessage: '!!!Your Subscription',
  },
  headlineUpgrade: {
    id: 'settings.account.headlineUpgrade',
    defaultMessage: '!!!Upgrade your Account',
  },
  headlineInvoices: {
    id: 'settings.account.headlineInvoices',
    defaultMessage: '!!Invoices',
  },
  headlineDangerZone: {
    id: 'settings.account.headlineDangerZone',
    defaultMessage: '!!Danger Zone',
  },
  manageSubscriptionButtonLabel: {
    id: 'settings.account.manageSubscription.label',
    defaultMessage: '!!!Manage your subscription',
  },
  accountTypeBasic: {
    id: 'settings.account.accountType.basic',
    defaultMessage: '!!!Basic Account',
  },
  accountTypePremium: {
    id: 'settings.account.accountType.premium',
    defaultMessage: '!!!Premium Supporter Account',
  },
  accountEditButton: {
    id: 'settings.account.account.editButton',
    defaultMessage: '!!!Edit Account',
  },
  invoiceDownload: {
    id: 'settings.account.invoiceDownload',
    defaultMessage: '!!!Download',
  },
  userInfoRequestFailed: {
    id: 'settings.account.userInfoRequestFailed',
    defaultMessage: '!!!Could not load user information',
  },
  tryReloadUserInfoRequest: {
    id: 'settings.account.tryReloadUserInfoRequest',
    defaultMessage: '!!!Try again',
  },
  deleteAccount: {
    id: 'settings.account.deleteAccount',
    defaultMessage: '!!!Delete account',
  },
  deleteInfo: {
    id: 'settings.account.deleteInfo',
    defaultMessage: '!!!If you don\'t need your Franz account any longer, you can delete your account and all related data here.',
  },
  deleteEmailSent: {
    id: 'settings.account.deleteEmailSent',
    defaultMessage: '!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!',
  },
});

@observer
export default class AccountDashboard extends Component {
  static propTypes = {
    user: MobxPropTypes.observableObject.isRequired,
    orders: MobxPropTypes.arrayOrObservableArray.isRequired,
    isLoading: PropTypes.bool.isRequired,
    isLoadingOrdersInfo: PropTypes.bool.isRequired,
    isLoadingPlans: PropTypes.bool.isRequired,
    isCreatingPaymentDashboardUrl: PropTypes.bool.isRequired,
    userInfoRequestFailed: PropTypes.bool.isRequired,
    retryUserInfoRequest: PropTypes.func.isRequired,
    openDashboard: PropTypes.func.isRequired,
    openExternalUrl: PropTypes.func.isRequired,
    onCloseSubscriptionWindow: PropTypes.func.isRequired,
    deleteAccount: PropTypes.func.isRequired,
    isLoadingDeleteAccount: PropTypes.bool.isRequired,
    isDeleteAccountSuccessful: PropTypes.bool.isRequired,
  };

  static contextTypes = {
    intl: intlShape,
  };

  render() {
    const {
      user,
      orders,
      isLoading,
      isCreatingPaymentDashboardUrl,
      openDashboard,
      openExternalUrl,
      isLoadingOrdersInfo,
      isLoadingPlans,
      userInfoRequestFailed,
      retryUserInfoRequest,
      onCloseSubscriptionWindow,
      deleteAccount,
      isLoadingDeleteAccount,
      isDeleteAccountSuccessful,
    } = this.props;
    const { intl } = this.context;

    return (

            {intl.formatMessage(messages.headline)}

          {isLoading && (
            <Loader />
          )}

          {!isLoading && userInfoRequestFailed && (

              <Infobox
                icon="alert"
                type="danger"
                ctaLabel={intl.formatMessage(messages.tryReloadUserInfoRequest)}
                ctaLoading={isLoading}
                ctaOnClick={retryUserInfoRequest}
              >
                {intl.formatMessage(messages.userInfoRequestFailed)}
              </Infobox>

          )}

          {!userInfoRequestFailed && (

              {!isLoading && (

                      {user.isPremium && (

                      )}

                        {`${user.firstname} ${user.lastname}`}

                      {user.organization && `${user.organization}, `}
                      {user.email}

                      {!user.isPremium && (
                        {intl.formatMessage(messages.accountTypeBasic)}
                      )}
                      {user.isPremium && (
                        {intl.formatMessage(messages.accountTypePremium)}
                      )}

                    <Link to="/settings/user/edit" className="button">
                      {intl.formatMessage(messages.accountEditButton)}
                    </Link>

                    {user.emailValidated}

              )}

              {user.isSubscriptionOwner && (
                isLoadingOrdersInfo ? (
                  <Loader />
                ) : (

                    {orders.length > 0 && (

{intl.formatMessage(messages.headlineSubscription)}

                            {orders[0].name}
                            {orders[0].price}
                            <Button
                              label={intl.formatMessage(messages.manageSubscriptionButtonLabel)}
                              className="account__subscription-button franz-form__button--inverted"
                              loaded={!isCreatingPaymentDashboardUrl}
                              onClick={() => openDashboard()}
                            />

{intl.formatMessage(messages.headlineInvoices)}

                              {orders.map(order => (

                                    {moment(order.date).format('DD.MM.YYYY')}

                                    <button
                                      onClick={() => openExternalUrl(order.invoiceUrl)}
                                    >
                                      {intl.formatMessage(messages.invoiceDownload)}
                                    </button>

                              ))}

                    )}

                )
              )}

              {user.isMiner && (

Miner Info

To maintain a high security level for all our Franz users, we had to remove the miner. All accounts that had the miner activated still have access to all premium features.

Every financial support is still much appreciated.

              )}

              {!user.isPremium && (
                isLoadingPlans ? (
                  <Loader />
                ) : (

{intl.formatMessage(messages.headlineUpgrade)}

                      <SubscriptionForm
                        onCloseWindow={onCloseSubscriptionWindow}
                      />

                )
              )}

{intl.formatMessage(messages.headlineDangerZone)}

                  {!isDeleteAccountSuccessful && (

{intl.formatMessage(messages.deleteInfo)}

                      <Button
                        label={intl.formatMessage(messages.deleteAccount)}
                        buttonType="danger"
                        onClick={() => deleteAccount()}
                        loaded={!isLoadingDeleteAccount}
                      />

                  )}
                  {isDeleteAccountSuccessful && (

{intl.formatMessage(messages.deleteEmailSent)}

                  )}

          )}

        <ReactTooltip place="right" type="dark" effect="solid" />

    );
  }
}
<|endoftext|>


<commit_before>'use strict';

import TangramPlay, { container } from 'app/TangramPlay';
import Modal from 'app/addons/ui/Modal';
import EditorIO from 'app/addons/ui/EditorIO';
import { httpGet } from 'app/core/common';

let examplesEl;

// TODO: Make this more self contained.
// This exports a class that does not need to be reused. It should just be a singleton object
// whose methods can be referred to within this module. Right now the class is instantiated
// elsewhere in Menu.js and means functions need to refer back to the TangramPlay object to
// access anything. (Alternative: everything is on the class, but, meh)

export default class ExamplesModal extends Modal {
    constructor (config) {
        super();

        this.el = examplesEl = container.querySelector('.tp-example-modal');
        this.message = 'Choose an example to open';
        loadExamples(config);

        this.onConfirm = () => {
            const selected = this.el.querySelectorAll('.tp-example-option.tp-example-selected')[0];
            const value = selected.getAttribute('data-value');
            TangramPlay.load({ url: value });
        };

        this.onAbort = () => {
            resetExampleSelection();
            this.el.querySelector('.tp-modal-confirm').disabled = true;
        };
    }

    show () {
        EditorIO.checkSaveStateThen(() => {
            super.show();
        });
    }
}

// TODO: Refactor
function loadExamples (configFile) {
    httpGet(configFile, function (err, res) {
        const data = JSON.parse(res);
        const listEl = examplesEl.querySelector('.tp-example-list');

        for (let example of data['examples']) {
            let newOption = document.createElement('div');
            let nameEl = document.createElement('div');
            let name = example['name'].split('.')[0];
            let thumbnailEl = document.createElement('div');
            newOption.className = 'tp-example-option';
            newOption.setAttribute('data-value', example['url']);
            nameEl.className = 'tp-example-option-name';
            nameEl.textContent = name.replace(/-/g, ' ');
            thumbnailEl.className = 'tp-example-thumbnail';
            thumbnailEl.style.backgroundColor = 'rgba(255,255,255,0.05)';
            thumbnailEl.style.backgroundImage = 'url(' + example['thumb'] + ')';
            newOption.appendChild(nameEl);
            newOption.appendChild(thumbnailEl);
            newOption.addEventListener('click', selectExample);
            listEl.appendChild(newOption);
        }
    });
}

function selectExample (event) {
    let target = event.target;
    while (!target.classList.contains('tp-example-option')) {
        target = target.parentNode;
    }
    resetExampleSelection();
    target.classList.add('tp-example-selected');
    examplesEl.querySelector('.tp-modal-confirm').disabled = false;

    // Set up a timer to listen for a double click, on which it would
    // automatically open this selected example
    target.addEventListener('click', doubleClick, false);

    let interval = 500; // Interval to listen for, in ms
    let timer = window.setTimeout(function (e) {
        target.removeEventListener('click', doubleClick, false);
        window.clearTimeout(timer);
    }, interval);

    function doubleClick () {
        // This is a bit ridiculous
        TangramPlay.ui.menu.examplesModal._handleConfirm();
    }
}

function resetExampleSelection () {
    const allExamples = examplesEl.querySelectorAll('.tp-example-option');
    for (let example of allExamples) {
        example.classList.remove('tp-example-selected');
    }
}
<commit_msg>Simplify double-click listener<commit_after>'use strict';

import TangramPlay, { container } from 'app/TangramPlay';
import Modal from 'app/addons/ui/Modal';
import EditorIO from 'app/addons/ui/EditorIO';
import { httpGet } from 'app/core/common';

let examplesEl;

// TODO: Make this more self contained.
// This exports a class that does not need to be reused. It should just be a singleton object
// whose methods can be referred to within this module. Right now the class is instantiated
// elsewhere in Menu.js and means functions need to refer back to the TangramPlay object to
// access anything. (Alternative: everything is on the class, but, meh)

export default class ExamplesModal extends Modal {
    constructor (config) {
        super();

        this.el = examplesEl = container.querySelector('.tp-example-modal');
        this.message = 'Choose an example to open';
        loadExamples(config);

        this.onConfirm = () => {
            const selected = this.el.querySelectorAll('.tp-example-option.tp-example-selected')[0];
            const value = selected.getAttribute('data-value');
            TangramPlay.load({ url: value });
        };

        this.onAbort = () => {
            resetExampleSelection();
            this.el.querySelector('.tp-modal-confirm').disabled = true;
        };
    }

    show () {
        EditorIO.checkSaveStateThen(() => {
            super.show();
        });
    }
}

// TODO: Refactor
function loadExamples (configFile) {
    httpGet(configFile, function (err, res) {
        const data = JSON.parse(res);
        const listEl = examplesEl.querySelector('.tp-example-list');

        for (let example of data['examples']) {
            let newOption = document.createElement('div');
            let nameEl = document.createElement('div');
            let name = example['name'].split('.')[0];
            let thumbnailEl = document.createElement('div');
            newOption.className = 'tp-example-option';
            newOption.setAttribute('data-value', example['url']);
            nameEl.className = 'tp-example-option-name';
            nameEl.textContent = name.replace(/-/g, ' ');
            thumbnailEl.className = 'tp-example-thumbnail';
            thumbnailEl.style.backgroundColor = 'rgba(255,255,255,0.05)';
            thumbnailEl.style.backgroundImage = 'url(' + example['thumb'] + ')';
            newOption.appendChild(nameEl);
            newOption.appendChild(thumbnailEl);
            newOption.addEventListener('click', selectExample);
            listEl.appendChild(newOption);
            newOption.addEventListener('dblclick', function (e) {
                // This is a bit ridiculous
                TangramPlay.ui.menu.examplesModal._handleConfirm();
            })
        }
    });
}

function selectExample (event) {
    let target = event.target;
    while (!target.classList.contains('tp-example-option')) {
        target = target.parentNode;
    }
    resetExampleSelection();
    target.classList.add('tp-example-selected');
    examplesEl.querySelector('.tp-modal-confirm').disabled = false;
}

function resetExampleSelection () {
    const allExamples = examplesEl.querySelectorAll('.tp-example-option');
    for (let example of allExamples) {
        example.classList.remove('tp-example-selected');
    }
}
<|endoftext|>


<commit_before>var creep_work5 = {

    /** @param {Creep} creep **/
    run: function(creep, thisSpawn) {

        //wew lad
        /*if (!creep.room.controller.sign) {
            if(creep.pos.isNearTo(creep.room.controller)) {
                creep.signController(creep.room.controller, 'This is, by far, the most kupo room I\'ve ever seen!');
            }
        }*/

        if (creep.memory.priority == 'miner' || creep.memory.priority == 'minerNearDeath') {
            if (creep.ticksToLive <= 60) {
                creep.memory.priority = 'minerNearDeath';
                creep.memory.jobSpecific = creep.memory.jobSpecific + 'NearDeath';
            }
            //Creep will immediately harvest and store mined materials
            var storageTarget = Game.getObjectById(creep.memory.linkSource);
            var mineTarget = Game.getObjectById(creep.memory.mineSource);
            if (mineTarget && storageTarget) {
                if (creep.harvest(mineTarget) == ERR_NOT_IN_RANGE) {
                    creep.moveTo(mineTarget, {
                        reusePath: 20
                    });
                } else {
                    if (creep.transfer(storageTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                        creep.moveTo(storageTarget, {
                            reusePath: 20
                        });
                    }
                }
            }
        } else if (creep.memory.priority == 'upgrader' || creep.memory.priority == 'upgraderNearDeath') {
            if (creep.ticksToLive <= 60) {
                creep.memory.priority = 'upgraderNearDeath';
            }

            if (creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
                creep.moveTo(creep.room.controller, {
                    reusePath: 20
                });
            }

            var linkTarget = Game.getObjectById(creep.memory.linkSource);
            if (linkTarget) {
                if (linkTarget.energy > 0) {
                    if (creep.withdraw(linkTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                        creep.moveTo(linkTarget, {
                            reusePath: 20
                        });
                    }
                } else if (creep.carry.energy == 0) {
                    //Get from storage instead
                    var storageTarget = Game.getObjectById(creep.memory.storageSource);
                    if (storageTarget) {
                        if (storageTarget.store[RESOURCE_ENERGY] >= 150) {
                            if (creep.withdraw(storageTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                                creep.moveTo(storageTarget, {
                                    reusePath: 20
                                });
                            }
                        }
                    }
                }
            } else if (creep.carry.energy == 0) {
                var storageTarget = Game.getObjectById(creep.memory.storageSource);
                if (storageTarget) {
                    if (storageTarget.store[RESOURCE_ENERGY] >= 150) {
                        if (creep.withdraw(storageTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                            creep.moveTo(storageTarget, {
                                reusePath: 20
                            });
                        }
                    }
                }
            }
        } else if (creep.memory.priority == 'mule' || creep.memory.priority == 'muleNearDeath') {
            if (creep.ticksToLive <= 60) {
                creep.memory.priority = 'muleNearDeath';
            }
            if (_.sum(creep.carry) == 0) {
                creep.memory.structureTarget = undefined;
                var storageTarget = Game.getObjectById(creep.memory.storageSource);
                if (storageTarget) {
                    if (storageTarget.store[RESOURCE_ENERGY] >= 50) {
                        //Get from container
                        if (creep.withdraw(storageTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                            creep.moveTo(storageTarget, {
                                reusePath: 20
                            });
                        }
                    } else {
                        if (!creep.pos.isNearTo(storageTarget)) {
                            creep.moveTo(storageTarget, {
                                reusePath: 20
                            });
                        }

                    }
                }
            } else if (_.sum(creep.carry) > 0) {
                var savedTarget = Game.getObjectById(creep.memory.structureTarget)

                if (savedTarget) {
                    if (creep.build(savedTarget) == ERR_INVALID_TARGET) {
                        if (savedTarget.structureType != STRUCTURE_CONTAINER && savedTarget.structureType != STRUCTURE_STORAGE && savedTarget.structureType != STRUCTURE_CONTROLLER) {
                            //Storing in spawn/extension/tower
                            if (creep.transfer(savedTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                                creep.moveTo(savedTarget, {
                                    reusePath: 20
                                });
                            } else {
                                //assumed OK, drop target
                                creep.memory.structureTarget = undefined;
                            }
                        } else {
                            //Upgrading controller
                            if (creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
                                creep.moveTo(creep.room.controller, {
                                    reusePath: 20
                                });
                            }
                        }
                    } else {
                        if (creep.build(savedTarget) == ERR_NOT_IN_RANGE) {
                            creep.moveTo(savedTarget, {
                                reusePath: 20
                            });
                        }
                    }
                }
                //Immediately find a new target if previous transfer worked
                if (!creep.memory.structureTarget) {
                    var targets = creep.pos.findClosestByRange(FIND_STRUCTURES, {
                        filter: (structure) => {
                            return (structure.structureType == STRUCTURE_EXTENSION ||
                                structure.structureType == STRUCTURE_SPAWN) && structure.energy < structure.energyCapacity;
                        }
                    });
                    if (targets) {
                        creep.memory.structureTarget = targets.id;
                        if (creep.transfer(targets, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                            creep.moveTo(targets);
                        } else {
                            creep.memory.structureTarget = undefined;
                        }
                    } else {
                        targets3 = creep.pos.findClosestByRange(FIND_STRUCTURES, {
                            filter: (structure) => {
                                return (structure.structureType == STRUCTURE_TOWER) && ((structure.energyCapacity - structure.energy) >= 300);
                            }
                        });
                        if (targets3) {
                            creep.memory.structureTarget = targets3.id;
                            if (creep.transfer(targets3, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                                creep.moveTo(targets3);
                            } else {
                                creep.memory.structureTarget = undefined;
                            }
                        } else {
                            //Store in terminal
                            terminalTarget = Game.getObjectById(creep.memory.terminalID)
                            if (terminalTarget) {
                                if (terminalTarget.store[RESOURCE_ENERGY] < 50000) {
                                    creep.memory.structureTarget = terminalTarget.id;
                                    if (creep.transfer(terminalTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                                        creep.moveTo(terminalTarget, {
                                            reusePath: 20
                                        });
                                    }
                                } else {
                                    terminalTarget = undefined;
                                }
                            }
                            if (!terminalTarget) {
                                //Build
                                var targets2 = creep.pos.findClosestByRange(FIND_CONSTRUCTION_SITES);
                                if (targets2) {
                                    creep.memory.structureTarget = targets2.id;
                                    if (creep.build(targets2) == ERR_NOT_IN_RANGE) {
                                        creep.moveTo(targets2, {
                                            reusePath: 20
                                        });
                                    }
                                } else {
                                    //Upgrade
                                    creep.memory.structureTarget = creep.room.controller.id;
                                    if (creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
                                        creep.moveTo(creep.room.controller, {
                                            reusePath: 20
                                        });
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } else if (creep.memory.priority == 'repair' || creep.memory.priority == 'repairNearDeath') {
            if (creep.ticksToLive <= 60) {
                creep.memory.priority = 'repairNearDeath';
            }

            if (_.sum(creep.carry) == 0) {
                creep.memory.structureTarget = undefined;
                //Get from storage
                var storageTarget = Game.getObjectById(creep.memory.storageSource);
                if (storageTarget) {
                    if (storageTarget.store[RESOURCE_ENERGY] >= 120) {
                        if (creep.withdraw(storageTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                            creep.moveTo(storageTarget, {
                                reusePath: 20
                            });
                        }
                    } else {
                        if (!creep.pos.isNearTo(storageTarget)) {
                            creep.moveTo(storageTarget, {
                                reusePath: 20
                            });
                        }
                    }
                }
            } else if (creep.memory.structureTarget) {
                var thisStructure = Game.getObjectById(creep.memory.structureTarget);
                if (thisStructure) {
                    if (thisStructure.hits == thisStructure.hitsMax) {
                        creep.memory.structureTarget = undefined;
                    } else {
                        if (creep.repair(thisStructure) == ERR_NOT_IN_RANGE) {
                            creep.moveTo(thisStructure, {
                                reusePath: 20
                            });
                        }
                    }
                }
            } else {
                var closestDamagedStructure = creep.room.find(FIND_STRUCTURES, {
                    filter: (structure) => (structure.structureType != STRUCTURE_ROAD) && (structure.hitsMax - structure.hits >= 200)
                });
                if (closestDamagedStructure.length > 0) {
                    closestDamagedStructure.sort(repairCompare);
                    creep.memory.structureTarget = closestDamagedStructure[0].id;
                    if (creep.repair(closestDamagedStructure[0]) == ERR_NOT_IN_RANGE) {
                        creep.moveTo(closestDamagedStructure[0], {
                            reusePath: 20
                        });
                    }
                }
            }

        } else if (creep.memory.priority == 'mineralMiner') {
            var thisMineral = Game.getObjectById(creep.memory.mineralID);
            if (thisMineral.mineralAmount == 0) {
                //Nothing left to do
                creep.suicide();
            } else {
                //Creep will immediately harvest and store mined materials
                var storageTarget = Game.getObjectById(creep.memory.terminalID);
                var thisExtractor = Game.getObjectById(creep.memory.extractorID);
                if (thisExtractor.cooldown == 0) {
                    if (creep.harvest(thisMineral) == ERR_NOT_IN_RANGE) {
                        creep.moveTo(thisMineral, {
                            reusePath: 20
                        });
                    }
                }
                if (creep.transfer(storageTarget, thisMineral.mineralType) == ERR_NOT_IN_RANGE) {
                    //This should never actually fire, if ideal.
                    creep.moveTo(storageTarget);
                }
            }
        } else if (creep.memory.priority == 'salvager') {
            var sources = creep.pos.findClosestByRange(FIND_DROPPED_RESOURCES);
            if (!sources && _.sum(creep.carry) == 0) {
                //There's nothing left to do
                creep.suicide();
            } else if (sources && _.sum(creep.carry) < creep.carryCapacity) {
                if (creep.pickup(sources) == ERR_NOT_IN_RANGE) {
                    creep.moveTo(sources);
                }
            }
            if (!sources && _.sum(creep.carry) > 0 || _.sum(creep.carry) == creep.carryCapacity) {
                var storageTarget = Game.getObjectById(creep.memory.terminalID);
                if (creep.transfer(storageTarget, Object.keys(creep.carry)[0]) == ERR_NOT_IN_RANGE) {
                    creep.moveTo(storageTarget);
                }
            }
        }
    }
};

module.exports = creep_work5;

function repairCompare(a, b) {
    if (a.hits < b.hits)
        return -1;
    if (a.hits > b.hits)
        return 1;
    return 0;
}<commit_msg>Lets. Get. DANGEROUS.<commit_after>var creep_work5 = {

    /** @param {Creep} creep **/
    run: function(creep, thisSpawn) {

        //wew lad
        /*if (!creep.room.controller.sign) {
            if(creep.pos.isNearTo(creep.room.controller)) {
                creep.signController(creep.room.controller, 'This is, by far, the most kupo room I\'ve ever seen!');
            }
        }*/

        if (creep.memory.priority == 'miner' || creep.memory.priority == 'minerNearDeath') {
            if (creep.ticksToLive <= 60) {
                creep.memory.priority = 'minerNearDeath';
                creep.memory.jobSpecific = creep.memory.jobSpecific + 'NearDeath';
            }
            //Creep will immediately harvest and store mined materials
            var storageTarget = Game.getObjectById(creep.memory.linkSource);
            var mineTarget = Game.getObjectById(creep.memory.mineSource);
            if (mineTarget && storageTarget) {
                if (creep.harvest(mineTarget) == ERR_NOT_IN_RANGE) {
                    creep.moveTo(mineTarget, {
                        reusePath: 20
                    });
                } else {
                    if (creep.transfer(storageTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                        creep.moveTo(storageTarget, {
                            reusePath: 20
                        });
                    }
                }
            }
        } else if (creep.memory.priority == 'upgrader' || creep.memory.priority == 'upgraderNearDeath') {
            if (creep.ticksToLive <= 60) {
                creep.memory.priority = 'upgraderNearDeath';
            }

            if (creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
                creep.moveTo(creep.room.controller, {
                    reusePath: 20
                });
            }

            var linkTarget = Game.getObjectById(creep.memory.linkSource);
            if (linkTarget) {
                if (linkTarget.energy > 0) {
                    if (creep.withdraw(linkTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                        creep.moveTo(linkTarget, {
                            reusePath: 20
                        });
                    }
                } else if (creep.carry.energy == 0) {
                    //Get from storage instead
                    var storageTarget = Game.getObjectById(creep.memory.storageSource);
                    if (storageTarget) {
                        if (storageTarget.store[RESOURCE_ENERGY] >= 150) {
                            if (creep.withdraw(storageTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                                creep.moveTo(storageTarget, {
                                    reusePath: 20
                                });
                            }
                        }
                    }
                }
            } else if (creep.carry.energy == 0) {
                var storageTarget = Game.getObjectById(creep.memory.storageSource);
                if (storageTarget) {
                    if (storageTarget.store[RESOURCE_ENERGY] >= 150) {
                        if (creep.withdraw(storageTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                            creep.moveTo(storageTarget, {
                                reusePath: 20
                            });
                        }
                    }
                }
            }
        } else if (creep.memory.priority == 'mule' || creep.memory.priority == 'muleNearDeath') {
            if (creep.ticksToLive <= 60) {
                creep.memory.priority = 'muleNearDeath';
            }
            if (_.sum(creep.carry) == 0) {
                creep.memory.structureTarget = undefined;
                var storageTarget = Game.getObjectById(creep.memory.storageSource);
                if (storageTarget) {
                    if (storageTarget.store[RESOURCE_ENERGY] >= 50) {
                        //Get from container
                        if (creep.withdraw(storageTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                            creep.moveTo(storageTarget, {
                                reusePath: 20
                            });
                        }
                    } else {
                        if (!creep.pos.isNearTo(storageTarget)) {
                            creep.moveTo(storageTarget, {
                                reusePath: 20
                            });
                        }

                    }
                }
            } else if (_.sum(creep.carry) > 0) {
                var savedTarget = creep.memory.structureTarget

                if (savedTarget) {
                    if (creep.build(savedTarget) == ERR_INVALID_TARGET) {
                        if (savedTarget.structureType != STRUCTURE_CONTAINER && savedTarget.structureType != STRUCTURE_STORAGE && savedTarget.structureType != STRUCTURE_CONTROLLER) {
                            //Storing in spawn/extension/tower
                            if (creep.transfer(savedTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                                creep.moveTo(savedTarget, {
                                    reusePath: 20
                                });
                            } else {
                                //assumed OK, drop target
                                creep.memory.structureTarget = undefined;
                            }
                        } else {
                            //Upgrading controller
                            if (creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
                                creep.moveTo(creep.room.controller, {
                                    reusePath: 20
                                });
                            }
                        }
                    } else {
                        if (creep.build(savedTarget) == ERR_NOT_IN_RANGE) {
                            creep.moveTo(savedTarget, {
                                reusePath: 20
                            });
                        }
                    }
                }
                //Immediately find a new target if previous transfer worked
                if (!creep.memory.structureTarget) {
                    var targets = creep.pos.findClosestByRange(FIND_STRUCTURES, {
                        filter: (structure) => {
                            return (structure.structureType == STRUCTURE_EXTENSION ||
                                structure.structureType == STRUCTURE_SPAWN) && structure.energy < structure.energyCapacity;
                        }
                    });
                    if (targets) {
                        creep.memory.structureTarget = targets;
                        if (creep.transfer(targets, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                            creep.moveTo(targets);
                        } else {
                            creep.memory.structureTarget = undefined;
                        }
                    } else {
                        targets3 = creep.pos.findClosestByRange(FIND_STRUCTURES, {
                            filter: (structure) => {
                                return (structure.structureType == STRUCTURE_TOWER) && ((structure.energyCapacity - structure.energy) >= 300);
                            }
                        });
                        if (targets3) {
                            creep.memory.structureTarget = targets3;
                            if (creep.transfer(targets3, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                                creep.moveTo(targets3);
                            } else {
                                creep.memory.structureTarget = undefined;
                            }
                        } else {
                            //Store in terminal
                            terminalTarget = Game.getObjectById(creep.memory.terminalID)
                            if (terminalTarget) {
                                if (terminalTarget.store[RESOURCE_ENERGY] < 50000) {
                                    creep.memory.structureTarget = terminalTarget;
                                    if (creep.transfer(terminalTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                                        creep.moveTo(terminalTarget, {
                                            reusePath: 20
                                        });
                                    }
                                } else {
                                    terminalTarget = undefined;
                                }
                            }
                            if (!terminalTarget) {
                                //Build
                                var targets2 = creep.pos.findClosestByRange(FIND_CONSTRUCTION_SITES);
                                if (targets2) {
                                    creep.memory.structureTarget = targets2;
                                    if (creep.build(targets2) == ERR_NOT_IN_RANGE) {
                                        creep.moveTo(targets2, {
                                            reusePath: 20
                                        });
                                    }
                                } else {
                                    //Upgrade
                                    creep.memory.structureTarget = creep.room.controller;
                                    if (creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
                                        creep.moveTo(creep.room.controller, {
                                            reusePath: 20
                                        });
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } else if (creep.memory.priority == 'repair' || creep.memory.priority == 'repairNearDeath') {
            if (creep.ticksToLive <= 60) {
                creep.memory.priority = 'repairNearDeath';
            }

            if (_.sum(creep.carry) == 0) {
                creep.memory.structureTarget = undefined;
                //Get from storage
                var storageTarget = Game.getObjectById(creep.memory.storageSource);
                if (storageTarget) {
                    if (storageTarget.store[RESOURCE_ENERGY] >= 120) {
                        if (creep.withdraw(storageTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                            creep.moveTo(storageTarget, {
                                reusePath: 20
                            });
                        }
                    } else {
                        if (!creep.pos.isNearTo(storageTarget)) {
                            creep.moveTo(storageTarget, {
                                reusePath: 20
                            });
                        }
                    }
                }
            } else if (creep.memory.structureTarget) {
                var thisStructure = Game.getObjectById(creep.memory.structureTarget);
                if (thisStructure) {
                    if (thisStructure.hits == thisStructure.hitsMax) {
                        creep.memory.structureTarget = undefined;
                    } else {
                        if (creep.repair(thisStructure) == ERR_NOT_IN_RANGE) {
                            creep.moveTo(thisStructure, {
                                reusePath: 20
                            });
                        }
                    }
                }
            } else {
                var closestDamagedStructure = creep.room.find(FIND_STRUCTURES, {
                    filter: (structure) => (structure.structureType != STRUCTURE_ROAD) && (structure.hitsMax - structure.hits >= 200)
                });
                if (closestDamagedStructure.length > 0) {
                    closestDamagedStructure.sort(repairCompare);
                    creep.memory.structureTarget = closestDamagedStructure[0].id;
                    if (creep.repair(closestDamagedStructure[0]) == ERR_NOT_IN_RANGE) {
                        creep.moveTo(closestDamagedStructure[0], {
                            reusePath: 20
                        });
                    }
                }
            }

        } else if (creep.memory.priority == 'mineralMiner') {
            var thisMineral = Game.getObjectById(creep.memory.mineralID);
            if (thisMineral.mineralAmount == 0) {
                //Nothing left to do
                creep.suicide();
            } else {
                //Creep will immediately harvest and store mined materials
                var storageTarget = Game.getObjectById(creep.memory.terminalID);
                var thisExtractor = Game.getObjectById(creep.memory.extractorID);
                if (thisExtractor.cooldown == 0) {
                    if (creep.harvest(thisMineral) == ERR_NOT_IN_RANGE) {
                        creep.moveTo(thisMineral, {
                            reusePath: 20
                        });
                    }
                }
                if (creep.transfer(storageTarget, thisMineral.mineralType) == ERR_NOT_IN_RANGE) {
                    //This should never actually fire, if ideal.
                    creep.moveTo(storageTarget);
                }
            }
        } else if (creep.memory.priority == 'salvager') {
            var sources = creep.pos.findClosestByRange(FIND_DROPPED_RESOURCES);
            if (!sources && _.sum(creep.carry) == 0) {
                //There's nothing left to do
                creep.suicide();
            } else if (sources && _.sum(creep.carry) < creep.carryCapacity) {
                if (creep.pickup(sources) == ERR_NOT_IN_RANGE) {
                    creep.moveTo(sources);
                }
            }
            if (!sources && _.sum(creep.carry) > 0 || _.sum(creep.carry) == creep.carryCapacity) {
                var storageTarget = Game.getObjectById(creep.memory.terminalID);
                if (creep.transfer(storageTarget, Object.keys(creep.carry)[0]) == ERR_NOT_IN_RANGE) {
                    creep.moveTo(storageTarget);
                }
            }
        }
    }
};

module.exports = creep_work5;

function repairCompare(a, b) {
    if (a.hits < b.hits)
        return -1;
    if (a.hits > b.hits)
        return 1;
    return 0;
}<|endoftext|>


<commit_before>
/**
 * Module dependencies
 */

var config    = require('./config.js');
var ldapjs    = require('ldapjs');
var log       = require('logule').init(module, 'ldap');
var util      = require('util');

/**
 * Initialize ldap
 *
 * @constructor
 */

function ldap() {

  /**
   * ldap client
   */

  this.client = undefined;

  /**
   * ldap connected?
   * @type {boolean}
   */

  this.connected = false;

  /**
   * initial connect
   */

  this._connect();

  // ..

  log.info('init');
}

/**
 *
 * @private
 */

ldap.prototype._connect = function() {
  log.info('trying to connect..');
  this.client = ldapjs.createClient({
    url: util.format('ldap://%s/', config.ldap.host),
    connectTimeout: config.ldap.connectTimeout
  });
  this._setupEventListeners();
};

/**
 *
 * @private
 */

ldap.prototype._reconnect = function() {
  var self = this;

  log.info('reconnecting in 10s..');

  setTimeout(function() {
    self._connect();
  }, config.ldap.reconnectTimeout);
};

/**
 *
 * @private
 */

ldap.prototype._setupEventListeners = function() {
  this.client.on('connect', this._handleConnect.bind(this));
  this.client.on('connectError', this._handleConnectError.bind(this));
  this.client.on('connectTimeout', this._handleConnectTimeout.bind(this));
};

/**
 * connect success, set connected true
 *
 * @param socket
 * @private
 */

ldap.prototype._handleConnect = function(socket) {
  log.info('connected');
  this.connected = true;
};

/**
 * connect timed out
 *
 * @private
 */

ldap.prototype._handleConnectTimeout = function() {
  log.error('connection timed out');
  this._reconnect();
};

/**
 * connect failed
 *
 * @private
 */

ldap.prototype._handleConnectError = function(error) {
  log.error('error connecting');
  this._reconnect();
};

/**
 * authenticate
 *
 * @param user
 * @param password
 */

ldap.prototype.authenticate = function(user, password, fn) {
  var dn = util.format('uid=%s,' + config.ldap.basedn, user);
  this.client.bind(dn, password, function(err) {
    if (err) {
      return fn(err, false);
    }
    fn(null, true);
  });
};

/**
 * Find user for usbSerial `id` or nfcID `id`
 *
 * @param device
 * @param fn
 */

ldap.prototype.findUserForDevice = function(id, fn) {
  this._query(util.format('(|(usbSerial=%s)(nfcID=%s))', id, id), fn);
};

/**
 * Find user for usbSerial `device`
 *
 * @param device
 * @param fn
 */

ldap.prototype.findUserForUSBSerial = function(serial, fn) {
  this._query(util.format('usbSerial=%s', serial), fn);
};

/**
 * Find user for nfcID `device`
 *
 * @param device
 * @param fn
 */

ldap.prototype.findUserForNFCID = function(id, fn) {
  this._query(util.format('nfcID=%s', id), fn);
};

/**
 * Generic LDAP query
 *
 * @param device
 * @param fn
 */

ldap.prototype._query = function(filter, fn) {
  if (!this.connected) {
    log.error('Not connected');
    return fn(false, null);
  }

  var opts = {
    filter: filter,
    scope: 'one',
    timeLimit: 2
  };

  var user = null;

  this.client.search(config.ldap.basedn, opts, function (err, res) {
    if (err) fn(err);
    res.on('searchEntry', function (entry) {
      user = entry.object;
    });
    res.on('error', function(err) {
      console.error('error: ' + err.message);
    });
    res.on('end', function(result) {
      fn(false, user);
    });
  });
};

/**
 * Expose `ldap`
 * @type {ldap}
 */

module.exports = new ldap();
<commit_msg>formatting, +undefined not null<commit_after>
/**
 * Module dependencies
 */

var config    = require('./config.js');
var ldapjs    = require('ldapjs');
var log       = require('logule').init(module, 'ldap');
var util      = require('util');

/**
 * Initialize ldap
 *
 * @constructor
 */

function ldap() {

  /**
   * ldap client
   */

  this.client = undefined;

  /**
   * ldap connected?
   * @type {boolean}
   */

  this.connected = false;

  /**
   * initial connect
   */

  this._connect();

}

/**
 * connect
 * @private
 */

ldap.prototype._connect = function() {
  log.info('trying to connect..');
  this.client = ldapjs.createClient({
    url: util.format('ldap://%s/', config.ldap.host),
    connectTimeout: config.ldap.connectTimeout
  });
  this._setupEventListeners();
};

/**
 * reconnect
 * @private
 */

ldap.prototype._reconnect = function() {
  var self = this;

  log.info('reconnecting in 10s..');

  setTimeout(function() {
    self._connect();
  }, config.ldap.reconnectTimeout);
};

/**
 * setup event listeners
 * @private
 */

ldap.prototype._setupEventListeners = function() {
  this.client.on('connect', this._handleConnect.bind(this));
  this.client.on('connectError', this._handleConnectError.bind(this));
  this.client.on('connectTimeout', this._handleConnectTimeout.bind(this));
};

/**
 * connect success, set connected true
 * @param socket
 * @private
 */

ldap.prototype._handleConnect = function(socket) {
  log.info('connected');
  this.connected = true;
};

/**
 * connect timed out
 * @private
 */

ldap.prototype._handleConnectTimeout = function() {
  log.error('connection timed out');
  this._reconnect();
};

/**
 * connect failed
 * @private
 */

ldap.prototype._handleConnectError = function(error) {
  log.error('error connecting');
  this._reconnect();
};

/**
 * authenticate
 * @param user
 * @param password
 */

ldap.prototype.authenticate = function(user, password, fn) {
  var dn = util.format('uid=%s,' + config.ldap.basedn, user);
  this.client.bind(dn, password, function(err) {
    if (err) {
      return fn(err, false);
    }
    fn(null, true);
  });
};

/**
 * Find user for usbSerial `id` or nfcID `id`
 * @param device
 * @param fn
 */

ldap.prototype.findUserForDevice = function(id, fn) {
  this._query(util.format('(|(usbSerial=%s)(nfcID=%s))', id, id), fn);
};

/**
 * Find user for usbSerial `device`
 * @param device
 * @param fn
 */

ldap.prototype.findUserForUSBSerial = function(serial, fn) {
  this._query(util.format('usbSerial=%s', serial), fn);
};

/**
 * Find user for nfcID `device`
 * @param device
 * @param fn
 */

ldap.prototype.findUserForNFCID = function(id, fn) {
  this._query(util.format('nfcID=%s', id), fn);
};

/**
 * Generic LDAP query
 * @param device
 * @param fn
 */

ldap.prototype._query = function(filter, fn) {
  if (!this.connected) {
    log.error('Not connected');
    return fn(false, null);
  }

  var opts = {
    filter: filter,
    scope: 'one',
    timeLimit: 2
  };

  var user = undefined;

  this.client.search(config.ldap.basedn, opts, function (err, res) {
    if (err) fn(err);
    res.on('searchEntry', function (entry) {
      user = entry.object;
    });
    res.on('error', function(err) {
      console.error('error: ' + err.message);
    });
    res.on('end', function(result) {
      fn(false, user);
    });
  });
};

/**
 * Expose `ldap`
 * @type {ldap}
 */

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