<commit_before>if(!window.multigraph) {
    window.multigraph = {};
}

if(!window.multigraph.ModelTool) {
    window.multigraph.ModelTool = {};
}

(function (ns) {
    "use strict";
    function AttrList(name) {
        var that = this,
        attr = new window.multigraph.ModelTool.Attr(name);

        var delegate = function (obj, func) {
            return function () { return obj[func].apply(that, arguments); };
        };

        for (var i in attr) {
            if (attr.hasOwnProperty(i)) {
                if (typeof(attr[i]) === 'function') {
                    this[i] = delegate(attr, i);
                }
            }
        }

        //added since it's hard to inherit these properties from attr
        //via delegation
        this.and = that;
        this.which = that;

        this.addTo = function (obj) {
            var prop,
            arr = [],
            actualList = {};
            if(!obj || typeof(obj) !== 'object') {
                throw new Error("AttrList: addTo method requires an object parameter");
            } else {
                actualList.pop = delegate(arr, "pop");

                actualList.add = function (obj) {
                    if ((that.validator())(obj)) {
                        arr.push(obj);
                        return this;
                    } else {
                        throw new Error(that.errorMessage());
                    }
                };

                actualList.at = function (index) {
                    if (index < 0 || index >= this.size()) {
                        throw new Error("AttrList: Index out of bounds");
                    }
                    return arr[index];
                };

                //to keep things more java-y
                actualList.get = this.at;

                actualList.size = function () {
                    return arr.length;
                };

                obj[name] = function() {
                    return actualList;
                };
            }
        };
    }

    //this needs to stay if we're going to use instanceof
    //but note we override all of the methods via delegation
    //so it's not doing anything except for making an AttrList
    //an instance of Attr
    AttrList.prototype = new window.multigraph.ModelTool.Attr(name);

    ns.AttrList = AttrList;
}(window.multigraph.ModelTool));<commit_msg>attach 'get' alias for 'at' function to 'actualList', not 'this'<commit_after>if(!window.multigraph) {
    window.multigraph = {};
}

if(!window.multigraph.ModelTool) {
    window.multigraph.ModelTool = {};
}

(function (ns) {
    "use strict";
    function AttrList(name) {
        var that = this,
        attr = new window.multigraph.ModelTool.Attr(name);

        var delegate = function (obj, func) {
            return function () { return obj[func].apply(that, arguments); };
        };

        for (var i in attr) {
            if (attr.hasOwnProperty(i)) {
                if (typeof(attr[i]) === 'function') {
                    this[i] = delegate(attr, i);
                }
            }
        }

        //added since it's hard to inherit these properties from attr
        //via delegation
        this.and = that;
        this.which = that;

        this.addTo = function (obj) {
            var prop,
            arr = [],
            actualList = {};
            if(!obj || typeof(obj) !== 'object') {
                throw new Error("AttrList: addTo method requires an object parameter");
            } else {
                actualList.pop = delegate(arr, "pop");

                actualList.add = function (obj) {
                    if ((that.validator())(obj)) {
                        arr.push(obj);
                        return this;
                    } else {
                        throw new Error(that.errorMessage());
                    }
                };

                actualList.at = function (index) {
                    if (index < 0 || index >= this.size()) {
                        throw new Error("AttrList: Index out of bounds");
                    }
                    return arr[index];
                };

                //to keep things more java-y
                actualList.get = actualList.at;

                actualList.size = function () {
                    return arr.length;
                };

                obj[name] = function() {
                    return actualList;
                };
            }
        };
    }

    //this needs to stay if we're going to use instanceof
    //but note we override all of the methods via delegation
    //so it's not doing anything except for making an AttrList
    //an instance of Attr
    AttrList.prototype = new window.multigraph.ModelTool.Attr(name);

    ns.AttrList = AttrList;
}(window.multigraph.ModelTool));<|endoftext|>


<commit_before>import React from 'react';
import Podcast from './podcast.js';
import Snackbar from 'material-ui/Snackbar';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import WelcomeScreen from './welcome-screen.js';
import PlasterUpdater from './plaster-updater.js';

export default class PlasterApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      podcastPath: '',
      snackbarOpen: false,
      snackbarMessage: '',
      snackbarDuration: 4000,
      errorOpen: false,
      errorMessage: ''
    };
    this.goHome = this.goHome.bind(this);
    this.setSnackbar = this.setSnackbar.bind(this);
    this.snackbarClose = this.snackbarClose.bind(this);
    this.setError = this.setError.bind(this);
    this.errorClose = this.errorClose.bind(this);
    this.updatePodcastPath = this.updatePodcastPath.bind(this);
  }
  componentDidMount() {
    var pathFromLocalStorage = localStorage.getItem('podcastDirectory');
    if (pathFromLocalStorage && pathFromLocalStorage !== 'null') {
      this.setState({podcastPath: pathFromLocalStorage});
    }
  }
  updatePodcastPath(p) {
    this.setState({podcastPath: p});
    localStorage.setItem('podcastDirectory', p);
  }
  goHome() {
    this.setState({podcastPath: ''});
    localStorage.setItem('podcastDirectory', null);
  }
  setSnackbar(message, duration) {
    this.setState({
      snackbarOpen: true,
      snackbarMessage: message,
      snackbarDuration: duration
    });
  }
  snackbarClose() {
    this.setState({
      snackbarOpen: false
    });
  }
  setError(message) {
    this.setState({
      errorOpen: true,
      errorMessage: message
    });
  }
  errorClose() {
    this.setState({
      errorOpen: false
    });
  }
  render() {
    var mainWindow = (
      <WelcomeScreen
        updatePodcastPath={this.updatePodcastPath}
        setSnackbar={this.setSnackbar}
        setError={this.setError}
      />
    );
    if (this.state.podcastPath) {
      mainWindow = (
        <Podcast
          directory={this.state.podcastPath}
          goHome={this.goHome}
          setSnackbar={this.setSnackbar}
          setError={this.setError}
        />
      );
    }
    const errorActions = [
      <FlatButton
        label="Ok"
        primary={true}
        onTouchTap={this.errorClose}
      />
    ];
    return (

        {mainWindow}
        <Snackbar
          open={this.state.snackbarOpen}
          message={this.state.snackbarMessage}
          autoHideDuration={this.state.snackbarDuration}
          onRequestClose={this.snackbarClose}
        />
        <Dialog
          actions={errorActions}
          modal={false}
          open={this.state.errorOpen}
          onRequestClose={this.errorClose}
        >{this.state.errorMessage}</Dialog>
        <PlasterUpdater />

    );
  }
}
<commit_msg>add progress indicator to snackbar if there is no duration set<commit_after>import React from 'react';
import Podcast from './podcast.js';
import Snackbar from 'material-ui/Snackbar';
import Dialog from 'material-ui/Dialog';
import LinearProgress from 'material-ui/LinearProgress';
import FlatButton from 'material-ui/FlatButton';
import WelcomeScreen from './welcome-screen.js';
import PlasterUpdater from './plaster-updater.js';

export default class PlasterApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      podcastPath: '',
      snackbarOpen: false,
      snackbarMessage: '',
      snackbarDuration: 4000,
      errorOpen: false,
      errorMessage: ''
    };
    this.goHome = this.goHome.bind(this);
    this.setSnackbar = this.setSnackbar.bind(this);
    this.snackbarClose = this.snackbarClose.bind(this);
    this.setError = this.setError.bind(this);
    this.errorClose = this.errorClose.bind(this);
    this.updatePodcastPath = this.updatePodcastPath.bind(this);
  }
  componentDidMount() {
    var pathFromLocalStorage = localStorage.getItem('podcastDirectory');
    if (pathFromLocalStorage && pathFromLocalStorage !== 'null') {
      this.setState({podcastPath: pathFromLocalStorage});
    }
  }
  updatePodcastPath(p) {
    this.setState({podcastPath: p});
    localStorage.setItem('podcastDirectory', p);
  }
  goHome() {
    this.setState({podcastPath: ''});
    localStorage.setItem('podcastDirectory', null);
  }
  setSnackbar(message, duration) {
    this.setState({
      snackbarOpen: true,
      snackbarMessage: message,
      snackbarDuration: duration
    });
  }
  snackbarClose() {
    this.setState({
      snackbarOpen: false
    });
  }
  setError(message) {
    this.setState({
      errorOpen: true,
      errorMessage: message
    });
  }
  errorClose() {
    this.setState({
      errorOpen: false
    });
  }
  render() {
    var mainWindow = (
      <WelcomeScreen
        updatePodcastPath={this.updatePodcastPath}
        setSnackbar={this.setSnackbar}
        setError={this.setError}
      />
    );
    if (this.state.podcastPath) {
      mainWindow = (
        <Podcast
          directory={this.state.podcastPath}
          goHome={this.goHome}
          setSnackbar={this.setSnackbar}
          setError={this.setError}
        />
      );
    }
    const errorActions = [
      <FlatButton
        label="Ok"
        primary={true}
        onTouchTap={this.errorClose}
      />
    ];
    var snackbarProgress = (

        <LinearProgress mode="indeterminate" />

    );
    var snackbarJsx = (

        {this.state.snackbarDuration ? null : snackbarProgress}
        {this.state.snackbarMessage}

    );
    return (

        {mainWindow}
        <Snackbar
          open={this.state.snackbarOpen}
          message={snackbarJsx}
          autoHideDuration={this.state.snackbarDuration}
          onRequestClose={this.snackbarClose}
        />
        <Dialog
          actions={errorActions}
          modal={false}
          open={this.state.errorOpen}
          onRequestClose={this.errorClose}
        >{this.state.errorMessage}</Dialog>
        <PlasterUpdater />

    );
  }
}
<|endoftext|>


<commit_before>import * as Weights from "../Weights.js";

export { Params, Layer };

/**
 * Embedding (2D) layer parameters.
 */
class Params extends Weights.Params {

  /**
   * @param {number} channelMultiplier
   *   Every input channel will be expanded into so many embedding channels. The outChannels (output channel count)
   * is always depending on channelMultiplier and equal to ( inChannels * channelMultiplier ). If null, it will be
   * extracted from inputFloat32Array (i.e. by evolution).
   *
   * @return {boolean} Return false, if initialization failed.
   *
   * @override
   */
  init( inputFloat32Array, byteOffsetBegin, inChannels, channelMultiplier = null ) {

//!!! ...unfinished...
// inverted residual connection (by add or by concatenate) ? (dense net)
// squeeze-and-excitation ?
// Shuffled Grouped Pointwise Convolution ... ? (by tf.gather() ?)

    let parameterMap = new Map( [
      [ Weights.Params.Keys.inChannels,        inChannels ],
      [ Weights.Params.Keys.channelMultiplier, channelMultiplier ],

      // For an embedding layer, its output channel count always depends on channelMultiplier.
      [ Weights.Params.Keys.outChannels,       Inifity ],
    ] );

    return super.init( inputFloat32Array, byteOffsetBegin, parameterMap );
  }

}

/**
 * An embedding layer contains one params (this.params) and inChannels embedding vocabulary tables.
 * Every input channel has one embedding vocabulary table. Every embedding vocabulary table has
 * vocabularyCountPerInputChannel vocabularies. Every vocabulary has channelMultiplier embedding channels.
 */
class Layer {

  /**
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpret as weights.
   *
   * @param {number} byteOffsetBegin
   *   The position to start to decode from the inputFloat32Array. This is relative to the inputFloat32Array.buffer
   * (not to the inputFloat32Array.byteOffset).
   *
   * @param {number} inChannels
   *   The input channel count.
   *
   * @param {number} vocabularyCountPerInputChannel
   *   Every input channel will have how many vocabularies. This is also vocabulary count per vocabulary table (because
   * every input channel has a vocabulary table). For an image data (R-G-B-A four channels), there will be 256
   * vocabularies per input channel because every channel is represented by one byte (8 bits) which has 2^8 = 256 kinds
   * of possible values.
   *
   * @param {number} channelMultiplier
   *   Every vocabulary will have how many embedding channels. Every input channel will be expanded into so many
   * embedding channels. The outChannels (output channel count) is always depending on channelMultiplier and equal
   * to ( inChannels * channelMultiplier ). If null, it will be extracted from inputFloat32Array (i.e. by evolution).
   *
   * @return {boolean} Return false, if initialization failed.
   */
  init( inputFloat32Array, byteOffsetBegin, inChannels, vocabularyCountPerInputChannel, channelMultiplier = null ) {

//!!! ...unfinished...
// inverted residual connection (by add or by concatenate) ? (dense net)
// squeeze-and-excitation ?
// Shuffled Grouped Pointwise Convolution ... ? (by tf.gather() ?)

    this.params = this.vocabularyTables = null; // So that distinguishable if re-initialization failed.

    this.vocabularyCountPerInputChannel = vocabularyCountPerInputChannel;

    this.params = new Weights.Params();
    if ( !this.params.init( inputFloat32Array, byteOffsetBegin, inChannels, channelMultiplier ) )
      return false;

    {
      this.vocabularyTables = new Array( inChannels );

      let channelMultiplierInFact = this.params.channelMultiplier;
      let nextByteOffsetBegin = this.params.defaultByteOffsetEnd;
      for ( let i = 0; i < inChannels; ++i ) {

        this.vocabularyTables[ i ] = new Weights.Base();
        if ( !this.vocabularyTables[ i ].init(
               inputFloat32Array, nextByteOffsetBegin, null, 0, [vocabularyCountPerInputChannel, channelMultiplierInFact] ) )
          return false;

        nextByteOffsetBegin = this.vocabularyTables[ i ].defaultByteOffsetEnd;
      });
    }

    return true;
  }

  isValid() {
    if ( this.params )
      if ( this.params.isValid() )
        if ( this.vocabularyTables )
          if ( this.vocabularyTables[ this.params.inChannels - 1 ] )
            if ( this.vocabularyTables[ this.params.inChannels - 1 ].isValid() )  // Every vocabulary table is valid.
              return true;
    return false;
  }

  get byteOffsetBegin() { return this.params.defaultByteOffsetBegin; }
  get byteOffsetEnd()   { return this.vocabularyTables[ this.params.inChannels - 1 ].defaultByteOffsetEnd; }
}
<commit_msg>Update Embedding2D.js<commit_after>import * as Weights from "../Weights.js";

export { Params, Layer };

/**
 * Embedding (2D) layer parameters.
 */
class Params extends Weights.Params {

  /**
   * @param {number} channelMultiplier
   *   Every input channel will be expanded into so many embedding channels. The outChannels (output channel count)
   * is always depending on channelMultiplier and equal to ( inChannels * channelMultiplier ). If null, it will be
   * extracted from inputFloat32Array (i.e. by evolution).
   *
   * @return {boolean} Return false, if initialization failed.
   *
   * @override
   */
  init( inputFloat32Array, byteOffsetBegin, inChannels, channelMultiplier = null ) {

//!!! ...unfinished...
// inverted residual connection (by add or by concatenate) ? (dense net)
// squeeze-and-excitation ?
// Shuffled Grouped Pointwise Convolution ... ? (by tf.gather() ?)

    let parameterMap = new Map( [
      [ Weights.Params.Keys.inChannels,        inChannels ],
      [ Weights.Params.Keys.channelMultiplier, channelMultiplier ],

      // For an embedding layer, its output channel count always depends on channelMultiplier.
      [ Weights.Params.Keys.outChannels,       Inifity ],
    ] );

    return super.init( inputFloat32Array, byteOffsetBegin, parameterMap );
  }

}

/**
 * An embedding layer contains one params (this.params) and inChannels embedding vocabulary tables.
 * Every input channel has one embedding vocabulary table. Every embedding vocabulary table has
 * vocabularyCountPerInputChannel vocabularies. Every vocabulary has channelMultiplier embedding channels.
 */
class Layer {

  /**
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpret as weights.
   *
   * @param {number} byteOffsetBegin
   *   The position to start to decode from the inputFloat32Array. This is relative to the inputFloat32Array.buffer
   * (not to the inputFloat32Array.byteOffset).
   *
   * @param {number} inChannels
   *   The input channel count.
   *
   * @param {number} vocabularyCountPerInputChannel
   *   Every input channel will have how many vocabularies. This is also vocabulary count per vocabulary table (because
   * every input channel has a vocabulary table). For an image data (R-G-B-A four channels), there will be 256
   * vocabularies per input channel because every channel is represented by one byte (8 bits) which has 2^8 = 256 kinds
   * of possible values.
   *
   * @param {number} channelMultiplier
   *   Every vocabulary will have how many embedding channels. Every input channel will be expanded into so many
   * embedding channels. The outChannels (output channel count) is always depending on channelMultiplier and equal
   * to ( inChannels * channelMultiplier ). If null, it will be extracted from inputFloat32Array (i.e. by evolution).
   *
   * @return {boolean} Return false, if initialization failed.
   */
  init( inputFloat32Array, byteOffsetBegin, inChannels, vocabularyCountPerInputChannel, channelMultiplier = null ) {

//!!! ...unfinished...
// inverted residual connection (by add or by concatenate) ? (dense net)
// squeeze-and-excitation ?
// Shuffled Grouped Pointwise Convolution ... ? (by tf.gather() ?)

    this.params = this.vocabularyTables = null; // So that distinguishable if re-initialization failed.

    this.vocabularyCountPerInputChannel = vocabularyCountPerInputChannel;

    this.params = new Params();
    if ( !this.params.init( inputFloat32Array, byteOffsetBegin, inChannels, channelMultiplier ) )
      return false;

    {
      this.vocabularyTables = new Array( inChannels );

      let channelMultiplierInFact = this.params.channelMultiplier;
      let nextByteOffsetBegin = this.params.defaultByteOffsetEnd;
      for ( let i = 0; i < inChannels; ++i ) {

        this.vocabularyTables[ i ] = new Weights.Base();
        if ( !this.vocabularyTables[ i ].init(
               inputFloat32Array, nextByteOffsetBegin, null, 0, [vocabularyCountPerInputChannel, channelMultiplierInFact] ) )
          return false;

        nextByteOffsetBegin = this.vocabularyTables[ i ].defaultByteOffsetEnd;
      });
    }

    return true;
  }

  isValid() {
    if ( this.params )
      if ( this.params.isValid() )
        if ( this.vocabularyTables )
          if ( this.vocabularyTables[ this.params.inChannels - 1 ] )
            if ( this.vocabularyTables[ this.params.inChannels - 1 ].isValid() )  // Every vocabulary table is valid.
              return true;
    return false;
  }

  get byteOffsetBegin() { return this.params.defaultByteOffsetBegin; }
  get byteOffsetEnd()   { return this.vocabularyTables[ this.params.inChannels - 1 ].defaultByteOffsetEnd; }
}
<|endoftext|>


<commit_before>/* eslint-disable no-magic-numbers, max-len, camelcase */
define([
    'dojo/Deferred',
    'dojo/has',
    'dojo/request/xhr',
    'dojo/_base/array',
    'dojo/_base/Color',
    'dojo/_base/lang',

    'esri/config',
    'esri/SpatialReference',
    'esri/symbols/CartographicLineSymbol',
    'esri/symbols/SimpleFillSymbol',
    'esri/symbols/SimpleLineSymbol',
    'esri/symbols/SimpleMarkerSymbol',
    'esri/tasks/GeometryService'
], function (
    Deferred,
    has,
    xhr,
    array,
    Color,
    lang,

    esriConfig,
    SpatialReference,
    CartographicLineSymbol,
    SimpleFillSymbol,
    SimpleLineSymbol,
    SimpleMarkerSymbol,
    GeometryService
) {
    var agsDomain;
    var AGRC = {};
    let mapDataLocation;
    if (has('agrc-build') === 'prod') {
        // *.utah.gov
        AGRC.apiKey = 'AGRC-D3CDE591211690';
        agsDomain = 'enviro.deq.utah.gov';
        // mapserv.utah.gov or enviro.deq.utah.gov
        AGRC.quadWord = 'result-table-secure-antenna';
        mapDataLocation = 'C:\\MapData';
    } else if (has('agrc-build') === 'stage') {
        // test.mapserv.utah.gov
        AGRC.apiKey = 'AGRC-AC122FA9671436';
        agsDomain = 'test.mapserv.utah.gov';
        esriConfig.defaults.io.corsEnabledServers.push(agsDomain);
        AGRC.quadWord = 'opera-event-little-pinball';
        mapDataLocation = 'C:\\forklift-stage\\data\\production';
    } else {
        // localhost
        xhr(require.baseUrl + 'secrets.json', {
            handleAs: 'json',
            sync: true
        }).then(function (secrets) {
            AGRC.quadWord = secrets.quadWord;
            AGRC.apiKey = secrets.apiKey;
        }, function () {
            throw 'Error getting secrets!';
        });
        agsDomain = 'localhost';
        mapDataLocation = 'C:\\forklift\\data\\production';
    }

    // force api to use CORS on mapserv thus removing the test request on app load
    // e.g. http://mapserv.utah.gov/ArcGIS/rest/info?f=json
    esriConfig.defaults.io.corsEnabledServers.push('mapserv.utah.gov');
    esriConfig.defaults.io.corsEnabledServers.push('api.mapserv.utah.gov');
    esriConfig.defaults.io.corsEnabledServers.push('gis.trustlands.utah.gov');

    var zoomColor = new Color([255, 255, 0]);
    var zoomFillColor = new Color(zoomColor.toRgb().concat([0.15]));
    var selectionColor = new Color([240, 18, 190]);
    var selectionFillColor = new Color(selectionColor.toRgb().concat([0.35]));

    var baseUrl = (agsDomain === '') ? '' : window.location.protocol + '//' + agsDomain;
    esriConfig.defaults.geometryService = new GeometryService(baseUrl + '/arcgis/rest/services/Geometry/GeometryServer');
    var deqServiceFolder = baseUrl + '/arcgis/rest/services/DEQEnviro';
    var secureUrl = deqServiceFolder + '/Secure/MapServer';
    window.AGRC = lang.mixin(AGRC, {
        // app: app.App
        //      global reference to App
        app: null,

        // appName: String
        //      name of the app used in permissionsproxy
        appName: 'deq',

        // user: Object
        //      set in App.js
        user: null,

        // version.: String
        //      The version number.
        version: '1.6.4',

        // popupDelay: Number
        //      The delay (in milliseconds) before a popup is shown on hover.
        popupDelay: 250,

        gridIdentifyHeight: 237,

        labelsMinScale: 250000,

        minBuffer: 0.1, // in miles

        defaultBaseMap: 'Lite',

        printMapTitle: 'Printed from the Utah DEQ Interactive Map',

        downloadDataPath: `${mapDataLocation}\\deqquerylayers.gdb`,

        // topics: Object
        //      The topic strings used in this app
        topics: {
            appMapReferenceLayerToggle: {
                addLayer: 'app/map/ReferenceLayerToggle.addLayer',
                toggleLayer: 'app/map/ReferenceLayerToggle.toggleLayer'
            },
            appQueryLayer: {
                addLayer: 'app/QueryLayer.addLayer',
                removeLayer: 'app/QueryLayer.removeLayer'
            },
            appMapMapController: {
                mapZoom: 'app/map/MapController.mapZoom',
                zoomToSearchGraphic: 'app/map/MapController.zoomToSearchGraphic',
                graphic: 'app/map/MapController.graphic',
                clearGraphics: 'app/map/MapController.clearGraphics',
                zoom: 'app/map/MapController.zoom',
                showHighlightedFeature: 'app/map/MapController.showHighlightedFeature'
            },
            appSearch: {
                featuresFound: 'app/search/Search.featuresFound',
                searchStarted: 'app/search/Search.searchStarted',
                searchError: 'app/search/Search.searchError',
                clear: 'app/search/Search.clear',
                identify: 'app/search/identify',
                onStreamSelect: 'app/search/onStreamSelect'
            },
            appResultLayer: {
                addLayer: 'app/search/ResultLayer.addLayer',
                removeLayer: 'app/search/ResultLayer.removeLayer',
                highlightFeature: 'app/search/ResultLayer.highlightFeature',
                clearSelection: 'app/search/ResultLayer.clearSelection',
                identifyFeature: 'app/search/ResultLayer.identifyFeature'
            },
            appSearchIdentifyPane: {
                backToResults: 'app/search/IdentifyPane.backToResults'
            },
            app: {
                hideGrid: 'app/App.hideGrid',
                showGrid: 'app/App.showGrid',

                // matches topic name in ijit/widgets/authentication/LoginRegister
                onSignInSuccess: 'LoginRegister/sign-in-success'
            },
            appSearchResultsGrid: {
                downloadFeaturesDefined: 'app/search/ResultsGrid.downloadFeaturesDefined'
            },
            appDownloadDownload: {
                clearSelection: 'app/download/Download.clearSelection'
            }
        },

        // urls: Object
        //      Urls for the project
        urls: {
            UtahPLSS: 'https://tiles.arcgis.com/tiles/99lidPhWCzftIe9K/arcgis/rest/services/UtahPLSS/VectorTileServer',
            DEQEnviro: deqServiceFolder + '/MapService/MapServer',
            json: 'webdata/DEQEnviro.json',
            secure: secureUrl,
            search: location.pathname.replace(/\/(src|dist)/, '') + 'api/search',
            download: deqServiceFolder + '/Toolbox/GPServer/Download',
            printProxy: 'https://us-central1-utahkoopserver.cloudfunctions.net/printproxy/14/arcgis/rest/services/GPServer/export',
            landOwnership: 'https://gis.trustlands.utah.gov/server/' +
                           'rest/services/Ownership/UT_SITLA_Ownership_LandOwnership_WM/MapServer'
        },

        // layerIndices: Object
        //      Indices of layers within map services.
        layerIndices: {
            streams: 0,
            environmentalCovenants: 1,
            huc: 2,
            indianTribal: 3,
            risk: {
                facilities: 4,
                waterPoints: 5,
                streams: 6,
                lakes: 7
            },
            searchStreams: 8,
            landOwnership: 0 // in the SITLA service
        },

        // fieldNames: {}
        fieldNames: {
            cities: {
                NAME: 'NAME'
            },
            counties: {
                NAME: 'NAME'
            },
            utah: {
                STATE: 'STATE'
            },
            queryLayers: {
                ADDRESS: 'ADDRESS',
                CITY: 'CITY',
                ID: 'ID',
                NAME: 'NAME',
                TYPE: 'TYPE',
                UNIQUE_ID: 'UNIQUE_ID',
                ENVIROAPPLABEL: 'ENVIROAPPLABEL',
                ENVIROAPPSYMBOL: 'ENVIROAPPSYMBOL',
                OBJECTID: 'OBJECTID'
            },
            searchStreams: {
                GNIS_Name: 'GNIS_Name',
                COUNTY: 'COUNTY'
            },
            nhd: {
                FCode: 'FCode',
                GNIS_Name: 'GNIS_Name'
            },
            zip: {
                ZIP5: 'ZIP5'
            }
        },

        // featureClassNames: {}
        featureClassNames: {
            counties: 'SGID10.BOUNDARIES.Counties',
            utah: 'SGID10.BOUNDARIES.Utah',
            city: 'SGID10.BOUNDARIES.Municipalities_Carto', // _Carto version to make sure that Draper is not split on county line
            zip: 'SGID10.BOUNDARIES.ZipCodes'
        },

        // TRSMinScaleLevel: Number
        //      The minimum scale level that the TRS Layer will draw.
        TRSMinScaleLevel: 10,

        // NHDMinScaleLevel: Number
        //      The minimum scale level that the nhd layer will draw.
        NHDMinScaleLevel: 13,

        // appJson: Object
        //      Cache for data returned by getAppJson
        appJson: null,

        // queryLayerNames: Object
        //      Lookup for query layer name by layer index
        //      Populated in getAppJson below
        queryLayerNames: null,

        // spatialReference SpatialReference
        //      The spatial reference of the map
        spatialReference: new SpatialReference(3857),

        // symbols: Object
        //      Graphic symbols used in this app
        symbols: {
            zoom: {
                polygon: new SimpleFillSymbol(
                    SimpleFillSymbol.STYLE_SOLID,
                    new SimpleLineSymbol(
                        SimpleLineSymbol.STYLE_SOLID,
                        zoomColor,
                        2
                    ),
                    zoomFillColor),
                point: new SimpleMarkerSymbol(
                    SimpleMarkerSymbol.STYLE_CIRCLE,
                    13,
                    new SimpleLineSymbol(
                        SimpleLineSymbol.STYLE_SOLID,
                        zoomColor,
                        2),
                    zoomFillColor),
                polyline: new SimpleLineSymbol(
                    SimpleLineSymbol.STYLE_SOLID,
                    zoomColor,
                    4
                )
            },
            resultSymbolOpacity: 0.65,
            colors: [
                [31, 120, 180],
                [51, 160, 44],
                [227, 26, 28],
                [255, 127, 0],
                [106, 61, 154],
                [253, 191, 111],
                [251, 154, 153],
                [178, 223, 138],
                [166, 206, 227],
                [202, 178, 214],
                [255, 255, 153],
                [177, 89, 40]
            ],
            selection: {
                polygon: new SimpleFillSymbol(
                    SimpleFillSymbol.STYLE_SOLID,
                    new CartographicLineSymbol(
                        CartographicLineSymbol.STYLE_SOLID,
                        selectionColor,
                        3,
                        CartographicLineSymbol.CAP_ROUND,
                        CartographicLineSymbol.JOIN_ROUND),
                    selectionFillColor
                ),
                point: new SimpleMarkerSymbol(
                    SimpleMarkerSymbol.STYLE_CIRCLE,
                    12,
                    new SimpleLineSymbol(
                        SimpleLineSymbol.STYLE_SOLID,
                        selectionColor,
                        2),
                    selectionFillColor
                )
            }
        },

        // messages: Object
        //      Messages and text used throughout the app
        messages: {
            noFeaturesFound: 'No results'
        },

        parameterNames: {
            output: 'output' // download service
        },

        getAppJson: function () {
            // summary:
            //      requests the DEQEnviro.json data and returns it, caches the results for future requests
            console.log('app.config::getAppJson', arguments);

            var def = new Deferred();

            if (this.appJson) {
                def.resolve(this.appJson);
            } else if (this.appJsonRequest) {
                // prevent simultanious requests
                this.appJsonRequest.then(() => {
                    def.resolve(this.appJson);
                });
            } else {
                // get a fresh copy of the json file on each load
                this.appJsonRequest = xhr(this.urls.json, {
                    handleAs: 'json'
                }).then((json) => {
                    this.queryLayerNames = {};
                    json.queryLayers.forEach((ql) => {
                        this.queryLayerNames[ql.index] = ql.name;
                    });
                    this.appJsonRequest = null;
                    def.resolve(this.appJson = json);
                });
            }

            return def.promise;
        },
        getQueryLayerByIndex: function (index) {
            // summary:
            //      description
            // index: String
            // console.log('app/config:getQueryLayerByIndex', arguments);

            return this.getDatasetByIndex(this.appJson.queryLayers, index);
        },
        getRelatedTableByIndex: function (index) {
            // summary:
            //      description
            // index: String
            // console.log('app/config:getRelatedTableByIndex', arguments);

            return this.getDatasetByIndex(this.appJson.relatedTables, index);
        },
        getDatasetByIndex: function (datasets, index) {
            // this could easily be done with one loop but I wanted to try
            // out memoization :)
            this._cached = this._cached || {};

            if (this._cached[index]) {
                return this._cached[index];
            }

            var returnLayer;
            array.some(datasets, function (ql) {
                if (ql.index === index) {
                    returnLayer = ql;

                    return true;
                }

                return false;
            });

            this._cached[index] = returnLayer;

            return returnLayer;
        }
    });

    return window.AGRC;
});
<commit_msg>fix: clean up risk layer indexes and update streams<commit_after>/* eslint-disable no-magic-numbers, max-len, camelcase */
define([
    'dojo/Deferred',
    'dojo/has',
    'dojo/request/xhr',
    'dojo/_base/array',
    'dojo/_base/Color',
    'dojo/_base/lang',

    'esri/config',
    'esri/SpatialReference',
    'esri/symbols/CartographicLineSymbol',
    'esri/symbols/SimpleFillSymbol',
    'esri/symbols/SimpleLineSymbol',
    'esri/symbols/SimpleMarkerSymbol',
    'esri/tasks/GeometryService'
], function (
    Deferred,
    has,
    xhr,
    array,
    Color,
    lang,

    esriConfig,
    SpatialReference,
    CartographicLineSymbol,
    SimpleFillSymbol,
    SimpleLineSymbol,
    SimpleMarkerSymbol,
    GeometryService
) {
    var agsDomain;
    var AGRC = {};
    let mapDataLocation;
    if (has('agrc-build') === 'prod') {
        // *.utah.gov
        AGRC.apiKey = 'AGRC-D3CDE591211690';
        agsDomain = 'enviro.deq.utah.gov';
        // mapserv.utah.gov or enviro.deq.utah.gov
        AGRC.quadWord = 'result-table-secure-antenna';
        mapDataLocation = 'C:\\MapData';
    } else if (has('agrc-build') === 'stage') {
        // test.mapserv.utah.gov
        AGRC.apiKey = 'AGRC-AC122FA9671436';
        agsDomain = 'test.mapserv.utah.gov';
        esriConfig.defaults.io.corsEnabledServers.push(agsDomain);
        AGRC.quadWord = 'opera-event-little-pinball';
        mapDataLocation = 'C:\\forklift-stage\\data\\production';
    } else {
        // localhost
        xhr(require.baseUrl + 'secrets.json', {
            handleAs: 'json',
            sync: true
        }).then(function (secrets) {
            AGRC.quadWord = secrets.quadWord;
            AGRC.apiKey = secrets.apiKey;
        }, function () {
            throw 'Error getting secrets!';
        });
        agsDomain = 'localhost';
        mapDataLocation = 'C:\\forklift\\data\\production';
    }

    // force api to use CORS on mapserv thus removing the test request on app load
    // e.g. http://mapserv.utah.gov/ArcGIS/rest/info?f=json
    esriConfig.defaults.io.corsEnabledServers.push('mapserv.utah.gov');
    esriConfig.defaults.io.corsEnabledServers.push('api.mapserv.utah.gov');
    esriConfig.defaults.io.corsEnabledServers.push('gis.trustlands.utah.gov');

    var zoomColor = new Color([255, 255, 0]);
    var zoomFillColor = new Color(zoomColor.toRgb().concat([0.15]));
    var selectionColor = new Color([240, 18, 190]);
    var selectionFillColor = new Color(selectionColor.toRgb().concat([0.35]));

    var baseUrl = (agsDomain === '') ? '' : window.location.protocol + '//' + agsDomain;
    esriConfig.defaults.geometryService = new GeometryService(baseUrl + '/arcgis/rest/services/Geometry/GeometryServer');
    var deqServiceFolder = baseUrl + '/arcgis/rest/services/DEQEnviro';
    var secureUrl = deqServiceFolder + '/Secure/MapServer';
    window.AGRC = lang.mixin(AGRC, {
        // app: app.App
        //      global reference to App
        app: null,

        // appName: String
        //      name of the app used in permissionsproxy
        appName: 'deq',

        // user: Object
        //      set in App.js
        user: null,

        // version.: String
        //      The version number.
        version: '1.6.4',

        // popupDelay: Number
        //      The delay (in milliseconds) before a popup is shown on hover.
        popupDelay: 250,

        gridIdentifyHeight: 237,

        labelsMinScale: 250000,

        minBuffer: 0.1, // in miles

        defaultBaseMap: 'Lite',

        printMapTitle: 'Printed from the Utah DEQ Interactive Map',

        downloadDataPath: `${mapDataLocation}\\deqquerylayers.gdb`,

        // topics: Object
        //      The topic strings used in this app
        topics: {
            appMapReferenceLayerToggle: {
                addLayer: 'app/map/ReferenceLayerToggle.addLayer',
                toggleLayer: 'app/map/ReferenceLayerToggle.toggleLayer'
            },
            appQueryLayer: {
                addLayer: 'app/QueryLayer.addLayer',
                removeLayer: 'app/QueryLayer.removeLayer'
            },
            appMapMapController: {
                mapZoom: 'app/map/MapController.mapZoom',
                zoomToSearchGraphic: 'app/map/MapController.zoomToSearchGraphic',
                graphic: 'app/map/MapController.graphic',
                clearGraphics: 'app/map/MapController.clearGraphics',
                zoom: 'app/map/MapController.zoom',
                showHighlightedFeature: 'app/map/MapController.showHighlightedFeature'
            },
            appSearch: {
                featuresFound: 'app/search/Search.featuresFound',
                searchStarted: 'app/search/Search.searchStarted',
                searchError: 'app/search/Search.searchError',
                clear: 'app/search/Search.clear',
                identify: 'app/search/identify',
                onStreamSelect: 'app/search/onStreamSelect'
            },
            appResultLayer: {
                addLayer: 'app/search/ResultLayer.addLayer',
                removeLayer: 'app/search/ResultLayer.removeLayer',
                highlightFeature: 'app/search/ResultLayer.highlightFeature',
                clearSelection: 'app/search/ResultLayer.clearSelection',
                identifyFeature: 'app/search/ResultLayer.identifyFeature'
            },
            appSearchIdentifyPane: {
                backToResults: 'app/search/IdentifyPane.backToResults'
            },
            app: {
                hideGrid: 'app/App.hideGrid',
                showGrid: 'app/App.showGrid',

                // matches topic name in ijit/widgets/authentication/LoginRegister
                onSignInSuccess: 'LoginRegister/sign-in-success'
            },
            appSearchResultsGrid: {
                downloadFeaturesDefined: 'app/search/ResultsGrid.downloadFeaturesDefined'
            },
            appDownloadDownload: {
                clearSelection: 'app/download/Download.clearSelection'
            }
        },

        // urls: Object
        //      Urls for the project
        urls: {
            UtahPLSS: 'https://tiles.arcgis.com/tiles/99lidPhWCzftIe9K/arcgis/rest/services/UtahPLSS/VectorTileServer',
            DEQEnviro: deqServiceFolder + '/MapService/MapServer',
            json: 'webdata/DEQEnviro.json',
            secure: secureUrl,
            search: location.pathname.replace(/\/(src|dist)/, '') + 'api/search',
            download: deqServiceFolder + '/Toolbox/GPServer/Download',
            printProxy: 'https://us-central1-utahkoopserver.cloudfunctions.net/printproxy/14/arcgis/rest/services/GPServer/export',
            landOwnership: 'https://gis.trustlands.utah.gov/server/' +
                           'rest/services/Ownership/UT_SITLA_Ownership_LandOwnership_WM/MapServer'
        },

        // layerIndices: Object
        //      Indices of layers within map services.
        layerIndices: {
            streams: 0,
            environmentalCovenants: 1,
            huc: 2,
            indianTribal: 3,
            searchStreams: 4,
            landOwnership: 0 // in the SITLA service
        },

        // fieldNames: {}
        fieldNames: {
            cities: {
                NAME: 'NAME'
            },
            counties: {
                NAME: 'NAME'
            },
            utah: {
                STATE: 'STATE'
            },
            queryLayers: {
                ADDRESS: 'ADDRESS',
                CITY: 'CITY',
                ID: 'ID',
                NAME: 'NAME',
                TYPE: 'TYPE',
                UNIQUE_ID: 'UNIQUE_ID',
                ENVIROAPPLABEL: 'ENVIROAPPLABEL',
                ENVIROAPPSYMBOL: 'ENVIROAPPSYMBOL',
                OBJECTID: 'OBJECTID'
            },
            searchStreams: {
                GNIS_Name: 'GNIS_Name',
                COUNTY: 'COUNTY'
            },
            nhd: {
                FCode: 'FCode',
                GNIS_Name: 'GNIS_Name'
            },
            zip: {
                ZIP5: 'ZIP5'
            }
        },

        // featureClassNames: {}
        featureClassNames: {
            counties: 'SGID10.BOUNDARIES.Counties',
            utah: 'SGID10.BOUNDARIES.Utah',
            city: 'SGID10.BOUNDARIES.Municipalities_Carto', // _Carto version to make sure that Draper is not split on county line
            zip: 'SGID10.BOUNDARIES.ZipCodes'
        },

        // TRSMinScaleLevel: Number
        //      The minimum scale level that the TRS Layer will draw.
        TRSMinScaleLevel: 10,

        // NHDMinScaleLevel: Number
        //      The minimum scale level that the nhd layer will draw.
        NHDMinScaleLevel: 13,

        // appJson: Object
        //      Cache for data returned by getAppJson
        appJson: null,

        // queryLayerNames: Object
        //      Lookup for query layer name by layer index
        //      Populated in getAppJson below
        queryLayerNames: null,

        // spatialReference SpatialReference
        //      The spatial reference of the map
        spatialReference: new SpatialReference(3857),

        // symbols: Object
        //      Graphic symbols used in this app
        symbols: {
            zoom: {
                polygon: new SimpleFillSymbol(
                    SimpleFillSymbol.STYLE_SOLID,
                    new SimpleLineSymbol(
                        SimpleLineSymbol.STYLE_SOLID,
                        zoomColor,
                        2
                    ),
                    zoomFillColor),
                point: new SimpleMarkerSymbol(
                    SimpleMarkerSymbol.STYLE_CIRCLE,
                    13,
                    new SimpleLineSymbol(
                        SimpleLineSymbol.STYLE_SOLID,
                        zoomColor,
                        2),
                    zoomFillColor),
                polyline: new SimpleLineSymbol(
                    SimpleLineSymbol.STYLE_SOLID,
                    zoomColor,
                    4
                )
            },
            resultSymbolOpacity: 0.65,
            colors: [
                [31, 120, 180],
                [51, 160, 44],
                [227, 26, 28],
                [255, 127, 0],
                [106, 61, 154],
                [253, 191, 111],
                [251, 154, 153],
                [178, 223, 138],
                [166, 206, 227],
                [202, 178, 214],
                [255, 255, 153],
                [177, 89, 40]
            ],
            selection: {
                polygon: new SimpleFillSymbol(
                    SimpleFillSymbol.STYLE_SOLID,
                    new CartographicLineSymbol(
                        CartographicLineSymbol.STYLE_SOLID,
                        selectionColor,
                        3,
                        CartographicLineSymbol.CAP_ROUND,
                        CartographicLineSymbol.JOIN_ROUND),
                    selectionFillColor
                ),
                point: new SimpleMarkerSymbol(
                    SimpleMarkerSymbol.STYLE_CIRCLE,
                    12,
                    new SimpleLineSymbol(
                        SimpleLineSymbol.STYLE_SOLID,
                        selectionColor,
                        2),
                    selectionFillColor
                )
            }
        },

        // messages: Object
        //      Messages and text used throughout the app
        messages: {
            noFeaturesFound: 'No results'
        },

        parameterNames: {
            output: 'output' // download service
        },

        getAppJson: function () {
            // summary:
            //      requests the DEQEnviro.json data and returns it, caches the results for future requests
            console.log('app.config::getAppJson', arguments);

            var def = new Deferred();

            if (this.appJson) {
                def.resolve(this.appJson);
            } else if (this.appJsonRequest) {
                // prevent simultanious requests
                this.appJsonRequest.then(() => {
                    def.resolve(this.appJson);
                });
            } else {
                // get a fresh copy of the json file on each load
                this.appJsonRequest = xhr(this.urls.json, {
                    handleAs: 'json'
                }).then((json) => {
                    this.queryLayerNames = {};
                    json.queryLayers.forEach((ql) => {
                        this.queryLayerNames[ql.index] = ql.name;
                    });
                    this.appJsonRequest = null;
                    def.resolve(this.appJson = json);
                });
            }

            return def.promise;
        },
        getQueryLayerByIndex: function (index) {
            // summary:
            //      description
            // index: String
            // console.log('app/config:getQueryLayerByIndex', arguments);

            return this.getDatasetByIndex(this.appJson.queryLayers, index);
        },
        getRelatedTableByIndex: function (index) {
            // summary:
            //      description
            // index: String
            // console.log('app/config:getRelatedTableByIndex', arguments);

            return this.getDatasetByIndex(this.appJson.relatedTables, index);
        },
        getDatasetByIndex: function (datasets, index) {
            // this could easily be done with one loop but I wanted to try
            // out memoization :)
            this._cached = this._cached || {};

            if (this._cached[index]) {
                return this._cached[index];
            }

            var returnLayer;
            array.some(datasets, function (ql) {
                if (ql.index === index) {
                    returnLayer = ql;

                    return true;
                }

                return false;
            });

            this._cached[index] = returnLayer;

            return returnLayer;
        }
    });

    return window.AGRC;
});
<|endoftext|>


<commit_before>module( 'core.node' );

test( 'createElement', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('div');
    equals(node.tagName,'div','空div ——tagname');
    equals(node.type,'element','空div ——节点类型');
    node = uNode.createElement('
sdfadf
');
    equals(node.tagName,'div','非空div——tagname');
    equals(node.children[0].data,'sdfadf','非空div——数据内容');
});

test( 'getNodeById', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('

sdfadf
');
    node = node.getNodeById('bb');
    equals(node.getAttr('id'),'bb','获取标签id');
    node = uNode.createElement('

sdfadf
');
    node = node.getNodeById('cc');
    equals(node.getAttr('id'),'cc','获取标签id');
});

test( 'getNodesByTagName', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('

sdfadf
');
    var nodelist = node.getNodesByTagName('div');
    equals(nodelist.length,2,'div节点列表长度');
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

sdfadf','innerHTML内容');
});

test( 'innerHTML', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('
sdfadf
');
    node.innerHTML('

');
    var nodelist =node.getNodesByTagName('div');
    equals(nodelist.length,3,'div节点列表长度');
    for(var i= 0,ci;ci=nodelist[i++];){
        ci.tagName = 'p';
    }
    equals(node.innerHTML(),'

','innerHTML内容');
    node = uNode.createElement('

');
    node.innerHTML('asdf');
    equals(node.innerHTML(),'asdf','innerHTML内容');
});

test( 'innerText', function() {
    var tmp = new UE.uNode.createElement('area');
    tmp.innerHTML('

');
    equals(tmp.innerText(),tmp,'标签类型特殊');
    tmp = new UE.uNode.createText('');
    tmp.innerHTML('

');
    equals(tmp.innerText(),tmp,'对象类型不为element');
    var uNode = UE.uNode;
    var node = uNode.createElement('
sdfadf
');
    node.innerHTML('
dfsdfsdfeeesdf
');
    equals(node.innerText(),'dfsdfsdfeeesdf','获取标签中纯文本');
    node.innerText('sdf');
    equals(node.innerHTML(),'sdf','设置文本节点');
});

test( 'getData', function() {
    var tmp = new UE.uNode.createElement('div');
    equals(tmp.getData(),'','element元素');
    tmp = new UE.uNode.createText('askdj');
    equals(tmp.getData(),"askdj",'其他类型');
});

test( 'appendChild && insertBefore', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('
sdfadf
');
    node.innerHTML('

');
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','补全html标签');
    var tmp = uNode.createElement('div');
    node.appendChild(tmp);
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','appendChild');
    node.insertBefore(tmp,node.firstChild());
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','insertBefore');
    node.appendChild(tmp);
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','appendChild');
});

test( 'replaceChild && setAttr', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('
sdfadf
');
    node.innerHTML('

');
    var tmp = uNode.createElement('p');
    tmp.setAttr({'class':'test','id':'aa'});
    node.insertBefore(tmp,node.lastChild());
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','setAttr不为空');
    node.replaceChild(uNode.createElement('div'),tmp);
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','replaceChild');

    node.removeChild(node.lastChild(),true);
    tmp = uNode.createElement('p');
    tmp.setAttr();
    node.insertAfter(tmp,node.lastChild());
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','setAttr为空');
    node.innerHTML('

');
    tmp = uNode.createElement('div');
    node.appendChild(tmp);
    node.replaceChild(node.firstChild(),tmp);
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','replaceChild');
});

test( 'insertAfter', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('
sdfadf
');
    node.innerHTML('

');
    var tmp = uNode.createElement('div');
    node.appendChild(tmp);
    node.insertAfter(tmp,node.firstChild());
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','在第一个子节点后插入');
});

test( 'getStyle', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('div');
    node.innerHTML('

');
    node = node.firstChild();
    equals(node.getStyle(''),'','空cssStyle');
    node.innerHTML('

');
    node = node.firstChild();
    equals(node.getStyle('border'),'1px solid #ccc','有border，取border样式');
    node.innerHTML('

');
    node = node.firstChild();
    equals(node.getStyle('color'),'','无color样式，取color样式');
    node.innerHTML('

');
    node = node.firstChild();
    equals(node.getStyle('border'),'1px solid #ccc','有2个样式，取其一');
});

test( 'setStyle', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('div');
    node.innerHTML('

');
    node = node.firstChild();
    node.setStyle('border','2px solid #ccc');
    equals(node.getAttr('style'),'border:2px solid #ccc;color:#ccc','修改样式中的一个');
    node.setStyle({
        'font':'12px',
        'background':'#ccc'
    });
    equals(node.getAttr('style'),'background:#ccc;font:12px;border:2px solid #ccc;color:#ccc','添加新样式');
    node.setStyle({
        'font':'',
        'background':'',
        'border':'',
        'color':''
    });
    equals(node.getAttr('style'),undefined,'清空样式');
    node.setStyle('border','');
    equals(node.getAttr('style'),"border:<script>alert("")</script>;",'脚本');
    equals(node.toHtml(),'

','脚本转html');
    node.innerHTML('
asdfasdfsdf
');
    node.removeChild(node.firstChild(),true);
    equals(node.toHtml(),'
asdfasdfsdf
','移除子节点');
});

test( 'getIndex', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('div');
    node.innerHTML('
asdfasdfsdf
')
    node.removeChild(node.firstChild(),true);
    var tmp = new UE.uNode.createElement('div');
    node.appendChild(tmp);
    equals(tmp.getIndex(),2,'节点索引');
});

test( 'traversal', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('div');
    node.innerHTML('
asdfasdfsdf
')
    var count = 0;
    node.traversal(function(node){
        count++;
    });
    equals(count,4);
    count = 0;
    node.traversal(function(node){
        if(node.type == 'text'){
            count++
        }
    });
    equals(count,2);
    node.traversal(function(node){
        if(node.type == 'text'){

            node.parentNode.removeChild(node)
        }
    });
    equals(node.toHtml(),'

');
    node.innerHTML('
asdfasdfsdf
');
    node.traversal(function(node){
        if(node.type == 'text'){
            var span = uNode.createElement('span');
            node.parentNode.insertBefore(span,node);
            span.appendChild(node);
        }
    });
    equals(node.toHtml(),'

asdfasdfsdf

');
});<commit_msg>by zhanyi<commit_after>module( 'core.node' );

test( 'createElement', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('div');
    equals(node.tagName,'div','空div ——tagname');
    equals(node.type,'element','空div ——节点类型');
    node = uNode.createElement('
sdfadf
');
    equals(node.tagName,'div','非空div——tagname');
    equals(node.children[0].data,'sdfadf','非空div——数据内容');
});

test( 'getNodeById', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('

sdfadf
');
    node = node.getNodeById('bb');
    equals(node.getAttr('id'),'bb','获取标签id');
    node = uNode.createElement('

sdfadf
');
    node = node.getNodeById('cc');
    equals(node.getAttr('id'),'cc','获取标签id');
});

test( 'getNodesByTagName', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('

sdfadf
');
    var nodelist = node.getNodesByTagName('div');
    equals(nodelist.length,2,'div节点列表长度');
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

sdfadf','innerHTML内容');
});

test( 'innerHTML', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('
sdfadf
');
    node.innerHTML('

');
    var nodelist =node.getNodesByTagName('div');
    equals(nodelist.length,3,'div节点列表长度');
    for(var i= 0,ci;ci=nodelist[i++];){
        ci.tagName = 'p';
    }
    equals(node.innerHTML(),'

','innerHTML内容');
    node = uNode.createElement('

');
    node.innerHTML('asdf');
    equals(node.innerHTML(),'asdf','innerHTML内容');
});

test( 'innerText', function() {
    var tmp = new UE.uNode.createElement('area');
    tmp.innerHTML('

');
    equals(tmp.innerText(),tmp,'标签类型特殊');
    tmp = new UE.uNode.createText('');
    tmp.innerHTML('

');
    equals(tmp.innerText(),tmp,'对象类型不为element');
    var uNode = UE.uNode;
    var node = uNode.createElement('
sdfadf
');
    node.innerHTML('
dfsdfsdfeeesdf
');
    equals(node.innerText(),'dfsdfsdfeeesdf','获取标签中纯文本');
    node.innerText('sdf');
    equals(node.innerHTML(),'sdf','设置文本节点');
});

test( 'getData', function() {
    var tmp = new UE.uNode.createElement('div');
    equals(tmp.getData(),'','element元素');
    tmp = new UE.uNode.createText('askdj');
    equals(tmp.getData(),"askdj",'其他类型');
});

test( 'appendChild && insertBefore', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('
sdfadf
');
    node.innerHTML('

');
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','补全html标签');
    var tmp = uNode.createElement('div');
    node.appendChild(tmp);
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','appendChild');
    node.insertBefore(tmp,node.firstChild());
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','insertBefore');
    node.appendChild(tmp);
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','appendChild');
});

test( 'replaceChild && setAttr', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('
sdfadf
');
    node.innerHTML('

');
    var tmp = uNode.createElement('p');
    tmp.setAttr({'class':'test','id':'aa'});
    node.insertBefore(tmp,node.lastChild());
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','setAttr不为空');
    node.replaceChild(uNode.createElement('div'),tmp);
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','replaceChild');

    node.removeChild(node.lastChild(),true);
    tmp = uNode.createElement('p');
    tmp.setAttr();
    node.insertAfter(tmp,node.lastChild());
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','setAttr为空');
    node.innerHTML('

');
    tmp = uNode.createElement('div');
    node.appendChild(tmp);
    node.replaceChild(node.firstChild(),tmp);
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','replaceChild');
});

test( 'insertAfter', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('
sdfadf
');
    node.innerHTML('

');
    var tmp = uNode.createElement('div');
    node.appendChild(tmp);
    node.insertAfter(tmp,node.firstChild());
    equals(node.innerHTML().replace(/[ ]+>/g,'>'),'

','在第一个子节点后插入');
});

test( 'getStyle', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('div');
    node.innerHTML('

');
    node = node.firstChild();
    equals(node.getStyle(''),'','空cssStyle');
    node.innerHTML('

');
    node = node.firstChild();
    equals(node.getStyle('border'),'1px solid #ccc','有border，取border样式');
    node.innerHTML('

');
    node = node.firstChild();
    equals(node.getStyle('color'),'','无color样式，取color样式');
    node.innerHTML('

');
    node = node.firstChild();
    equals(node.getStyle('border'),'1px solid #ccc','有2个样式，取其一');
});

test( 'setStyle', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('div');
    node.innerHTML('

');
    node = node.firstChild();
    node.setStyle('border','2px solid #ccc');
    equals(node.getAttr('style'),'border:2px solid #ccc;color:#ccc','修改样式中的一个');
    node.setStyle({
        'font':'12px',
        'background':'#ccc'
    });
    equals(node.getAttr('style'),'background:#ccc;font:12px;border:2px solid #ccc;color:#ccc','添加新样式');
    node.setStyle({
        'font':'',
        'background':'',
        'border':'',
        'color':''
    });
    equals(node.getAttr('style'),undefined,'清空样式');
    node.setStyle('border','');
    equals(node.getAttr('style'),"border:<script>alert("")</script>;",'脚本');
    equals(node.toHtml(),'

','脚本转html');
    node.innerHTML('
asdfasdfsdf
');
    node.removeChild(node.firstChild(),true);
    equals(node.toHtml(),'
asdfasdfsdf
','移除子节点');
    node.innerHTML('

');
    node.firstChild().setStyle('border');
    equals(node.firstChild().toHtml(),'

','删除分号');
    node.innerHTML('

');
    equals(node.firstChild().toHtml(),'

');
});

test( 'getIndex', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('div');
    node.innerHTML('
asdfasdfsdf
')
    node.removeChild(node.firstChild(),true);
    var tmp = new UE.uNode.createElement('div');
    node.appendChild(tmp);
    equals(tmp.getIndex(),2,'节点索引');
});

test( 'traversal', function() {
    var uNode = UE.uNode;
    var node = uNode.createElement('div');
    node.innerHTML('
asdfasdfsdf
')
    var count = 0;
    node.traversal(function(node){
        count++;
    });
    equals(count,4);
    count = 0;
    node.traversal(function(node){
        if(node.type == 'text'){
            count++
        }
    });
    equals(count,2);
    node.traversal(function(node){
        if(node.type == 'text'){

            node.parentNode.removeChild(node)
        }
    });
    equals(node.toHtml(),'

');
    node.innerHTML('
asdfasdfsdf
');
    node.traversal(function(node){
        if(node.type == 'text'){
            var span = uNode.createElement('span');
            node.parentNode.insertBefore(span,node);
            span.appendChild(node);
        }
    });
    equals(node.toHtml(),'

asdfasdfsdf

');
});<|endoftext|>


<commit_before>(function($) {
	//there should be 2 input elements - 1 for text and one for textarea
	$.fn.typingtutor = function(options) {
		//todo: validate input
		var settings = $.extend({
			speedInterval: 4
		}, options);
		//todo: make order independent
		var text = this[0];
		var textarea = this[1];
		$(textarea).css('resize', 'none');
		$(textarea).attr('onpaste', 'return false;');
		$(textarea).attr('oncut', 'return false;');

		var currentLinePosition = 0;
		var lines = $(text).find('p');
		var originalTexts = [];
		var lineLetters = [];
		var totalCharacters = 0;
		for (var i = 0; i < lines.length; i++) {
			lines[i] = $(lines[i]);
			var lineText = $.trim(lines[i].text()).replace(/\t/g, ' ');
			totalCharacters += lineText.length;
			lines[i][0].innerHTML = '';
			originalTexts[i] = lineText;
			lineLetters[i] = [];
			for (var j = 0; j < lineText.length; j++) {
				var letter = $('' + lineText.charAt(j) + '');
				lines[i].append(letter);
				lineLetters[i][j] = letter;
			}
			//append a whitespace to the end of each line
			var lastLetter = $(' ');
			lineLetters[i][j] = lastLetter;
			lines[i].append(lastLetter);
			originalTexts[i] += ' ';
		}
		totalCharacters -= 1; //substracting 1 for the very first hit

		$(text).css('font-family', '"Courier New", Courier, monospace');

		drawCursor(0, 0);

		var textBgColor = 'rgb(201, 255, 224)';
		var whiteColor = 'rgb(255, 255, 255)';
		var cursorColor = 'rgb(198, 222, 162)';
		var errorColor = 'rgb(255, 97, 136)';

		function drawTextBackground(line, position) {
			if (lineLetters[line] && lineLetters[line][position]) {
				lineLetters[line][position].css('background-color', textBgColor);
			}
		}
		function clearTextBackground(line, position) {
			if (lineLetters[line] && lineLetters[line][position]) {
				lineLetters[line][position].css('background-color', whiteColor);
			}
		}
		function drawCursor(line, position) {
			if (lineLetters[line] && lineLetters[line][position]) {
				lineLetters[line][position].css('background-color', cursorColor);
			}
		}
		function increaseErrorCount(){
			isError = true;
			ec++;
			if (eh) {
				eh.call(this, ec);
			}
		}
		function highlightError(line, position) {
			if (lineLetters[line] && lineLetters[line][position]) {
				lineLetters[line][position].css('background-color', errorColor);
			}
			clearSpeed();
			increaseErrorCount();
		}
		function endsWith(str, suffix) {
			return str.indexOf(suffix, str.length - suffix.length) !== -1;
		}
		function getLines() {
			var currentText = textarea.value;
			var allLines = currentText.split(/\n/g);
			if ($.browser.name === 'msie' && endsWith(currentText, '\n')) {
				//todo test for ie > 8
				allLines.push('');
			}
			return allLines;
		}
		function getLinesCount() {
			return getLines().length;
		}
		function getLastLine() {
			var allLines = getLines();
			return allLines[allLines.length - 1];
		}

		//speed interval
		var si = settings.speedInterval;
		//last correct date
		var lcd;
		//times
		var tms = [];
		//speed callback
		var scb = settings.speedTrackCallback;
		//finish callback
		var fcb = settings.finishCallback;
		function clearSpeed() {
			lcd = null;
			tms.length = 0;
		}
		var millisInMinute = 1000 * 60;
		var startTime = null;

		//error count
		var ec = 0;
		var eh = settings.errorCallback;

		var isError = false;
		var keyPress = function(e) {
			if (!startTime) {
				startTime = new Date().getTime();
			}
			if (e.keyCode === 8 || e.keyCode === 13) {
				return;
			}

			//a letter has been typed
			var lastTypedLine = getLastLine();
			var currentTypingPosition = lastTypedLine.length;
			if (!originalTexts[currentLinePosition]) {
				return;
			}
			if (currentTypingPosition >= originalTexts[currentLinePosition].length - 1) {
				//at the end of the line
				if (e.which === 32) {
					//trigger enter event
					e.preventDefault();
					e = new jQuery.Event('keydown');
					e.which = e.keyCode = 13;
					$(textarea).trigger(e);

					$(textarea).val($(textarea).val() + '\n');
					return;
				}
			}
			if (currentTypingPosition > originalTexts[currentLinePosition].length - 1) {
				increaseErrorCount();
				return;
			}
			if (originalTexts[currentLinePosition].substring(0, currentTypingPosition + 1) === lastTypedLine + String.fromCharCode(e.which)) {
				//a correct letter is about to be pressed
				isError = false;
				drawTextBackground(currentLinePosition, currentTypingPosition);
				drawCursor(currentLinePosition, currentTypingPosition + 1);
				if (scb) {
					//correct date
					var cd = new Date().getTime();
					if (lcd) {
						//recording current time
						var t = cd - lcd;
						tms.push(t);
						if (tms.length === si) {
							//calculate average and call speed feedback
							var tot = 0;
							$.each(tms, function(i, val) {
								tot += val;
							});
							//average speed of typing one character
							var avg = tot / (tms.length);

							var speed = millisInMinute / avg;
							scb.call(this, parseInt(speed));

							tms.length = 0;
						}
					}

					lcd = cd;
				}
				//check if the whole text is typed
				//at this point we are sure that the typed symbol was correct
				//if we're at the end of last line - fire 'finishedcallback'
				if (fcb) {
					if (!isError && originalTexts.length - 1 === currentLinePosition && originalTexts[originalTexts.length - 1].length - 2/*substracting last whitespace*/ === currentTypingPosition) {
						var time = new Date().getTime() - startTime;
						var overallSpeed = (totalCharacters / time) * millisInMinute;
						fcb.call(this, parseInt(overallSpeed));
						//append last typed char to the textarea before disabling it
						$(textarea).val($(textarea).val() + String.fromCharCode(e.which));
						$(textarea).attr('disabled', true);
						$(textarea).unbind('keypress');
						$(textarea).unbind('keydown');
						$(textarea).unbind('keyup');
					}
				}
			} else {
				highlightError(currentLinePosition, currentTypingPosition);
			}
		};
		var keyDown = function(e) {
			//skip arrow keys
			if($.inArray(e.keyCode, [37, 38, 39, 40]) !== -1){
				e.preventDefault();
				return;
			}
			//disable end key, allow only shift+end combo
			if(e.keyCode === 36 && !e.shiftKey){
			    e.preventDefault();
			    return;
			}
			if (e.keyCode === 8) {
				//backspace - unstyle last styled character
				var lastLine = getLastLine();
				var currentTypingPosition = lastLine.length;
				if (currentTypingPosition > 0) {
					if (!originalTexts[currentLinePosition]) {
						return;
					}

					if (currentTypingPosition < originalTexts[currentLinePosition].length) {
						clearTextBackground(currentLinePosition, currentTypingPosition);
					}
					if (currentTypingPosition > 1) {
						if (currentTypingPosition - 1 < originalTexts[currentLinePosition].length) {
							clearTextBackground(currentLinePosition, currentTypingPosition - 1);
							if (originalTexts[currentLinePosition].substring(0, currentTypingPosition - 1) === lastLine.substring(0, currentTypingPosition - 1)) {
								drawCursor(currentLinePosition, currentTypingPosition - 1);
							} else {
								highlightError(currentLinePosition, currentTypingPosition - 2);
							}
						}
					} else {
						drawCursor(currentLinePosition, 0);
					}
				} else {
					//we may have jumped to a previous line
					//Remove cursor from previous line
					if (currentLinePosition === 0) {
						//we're at the beginning of the first line
						return;
					}
					if(! originalTexts[currentLinePosition]){
						return;
					}

					clearTextBackground(currentLinePosition, 0);
					currentLinePosition--;
					var currentTypedLine = getLines()[currentLinePosition];
					currentTypingPosition = currentTypedLine.length + 1; //adding one for space at the end
					if (checkInput(currentTypedLine, currentLinePosition, currentTypingPosition)) {
						drawCursor(currentLinePosition, currentTypingPosition - 1);
					} else {
						highlightError(currentLinePosition, currentTypingPosition - 2);
					}
				}
			} else if (e.keyCode === 13) {
				//enter
				//validate current line
				var lastLine = getLastLine();
				if ($.trim(originalTexts[currentLinePosition]) !== lastLine) {
					highlightError(currentLinePosition, lastLine.length);
					e.preventDefault();
					return;
				} else {
					drawTextBackground(currentLinePosition, lastLine.length);
				}
				currentLinePosition++;
				drawCursor(currentLinePosition, 0);
			}
		};
		var keyUp = function(e){
			//var t1 = new Date().getTime();
			//clear backgrounds of of all characters which are beyond current typing point
			var lastLine = getLastLine();
			var currentTypingPosition = lastLine.length;
			if(currentTypingPosition >= lineLetters[currentLinePosition].length){
				return;
			}
			//adding one for cursor
			for(var i = currentTypingPosition + 1; i < lineLetters[currentLinePosition].length; i++){
				clearTextBackground(currentLinePosition, i);
			}
			//we don't want to replace errors with cursor
			if ((e.ctrlKey || e.shiftKey || e.keyCode === 46) && checkInput(lastLine, currentLinePosition, currentTypingPosition)) {
				drawCursor(currentLinePosition, currentTypingPosition);
			}
			//console.log('KeyUp in: ' + (new Date().getTime() - t1));
		};

		$(textarea).keypress(keyPress);
		$(textarea).keydown(keyDown);
		$(textarea).keyup(keyUp);

		function moveToEnd() {
			var text = $(textarea).val();
			$(textarea).focus().val('').val(text);
		}
		$(textarea).mouseup(moveToEnd);

		/**
		 * Checks if last line was correct up to currentTypingPosition
		 */
		function checkInput(lastLine, currentLinePosition, currentTypingPosition){
			return $.trim(originalTexts[currentLinePosition]).substring(0, currentTypingPosition - 1) === $.trim(lastLine).substring(0, currentTypingPosition - 1);
		}
		$(textarea).focus();
		drawCursor(0, 0);

		//resulting object
		var res = {};
		res.restart = function(){
			$(textarea).unbind('keypress');
			$(textarea).unbind('keydown');
			$(textarea).unbind('keyup');
			//clear backgrounds of all texts
			$.each(lineLetters, function(i, line){
				$.each(line, function(j, letter){
					letter.css('background-color', whiteColor);
				});
			});
			drawCursor(0, 0);

			currentLinePosition = 0;

			$(textarea).val('').attr('disabled', false).focus();

			ec = 0;

			if (eh) {
				eh.call(this, ec);
			}

			isError = false;

			if (scb) {
				scb.call(this, 0);
			}
			clearSpeed();
			startTime = null;

			$(textarea).keypress(keyPress);
			$(textarea).keydown(keyDown);
			$(textarea).keyup(keyUp);
		};

		return res;
	};
}(jQuery));<commit_msg>disabled spell check for html 5<commit_after>(function($) {
	//there should be 2 input elements - 1 for text and one for textarea
	$.fn.typingtutor = function(options) {
		//todo: validate input
		var settings = $.extend({
			speedInterval: 4
		}, options);
		//todo: make order independent
		var text = this[0];
		var textarea = this[1];
		$(textarea).css('resize', 'none');
		$(textarea).attr('onpaste', 'return false;');
		$(textarea).attr('oncut', 'return false;');
		//hopefully the browser will support HTML5
		$(textarea).attr('spellcheck', 'false');

		var currentLinePosition = 0;
		var lines = $(text).find('p');
		var originalTexts = [];
		var lineLetters = [];
		var totalCharacters = 0;
		for (var i = 0; i < lines.length; i++) {
			lines[i] = $(lines[i]);
			var lineText = $.trim(lines[i].text()).replace(/\t/g, ' ');
			totalCharacters += lineText.length;
			lines[i][0].innerHTML = '';
			originalTexts[i] = lineText;
			lineLetters[i] = [];
			for (var j = 0; j < lineText.length; j++) {
				var letter = $('' + lineText.charAt(j) + '');
				lines[i].append(letter);
				lineLetters[i][j] = letter;
			}
			//append a whitespace to the end of each line
			var lastLetter = $(' ');
			lineLetters[i][j] = lastLetter;
			lines[i].append(lastLetter);
			originalTexts[i] += ' ';
		}
		totalCharacters -= 1; //substracting 1 for the very first hit

		$(text).css('font-family', '"Courier New", Courier, monospace');

		drawCursor(0, 0);

		var textBgColor = 'rgb(201, 255, 224)';
		var whiteColor = 'rgb(255, 255, 255)';
		var cursorColor = 'rgb(198, 222, 162)';
		var errorColor = 'rgb(255, 97, 136)';

		function drawTextBackground(line, position) {
			if (lineLetters[line] && lineLetters[line][position]) {
				lineLetters[line][position].css('background-color', textBgColor);
			}
		}
		function clearTextBackground(line, position) {
			if (lineLetters[line] && lineLetters[line][position]) {
				lineLetters[line][position].css('background-color', whiteColor);
			}
		}
		function drawCursor(line, position) {
			if (lineLetters[line] && lineLetters[line][position]) {
				lineLetters[line][position].css('background-color', cursorColor);
			}
		}
		function increaseErrorCount(){
			isError = true;
			ec++;
			if (eh) {
				eh.call(this, ec);
			}
		}
		function highlightError(line, position) {
			if (lineLetters[line] && lineLetters[line][position]) {
				lineLetters[line][position].css('background-color', errorColor);
			}
			clearSpeed();
			increaseErrorCount();
		}
		function endsWith(str, suffix) {
			return str.indexOf(suffix, str.length - suffix.length) !== -1;
		}
		function getLines() {
			var currentText = textarea.value;
			var allLines = currentText.split(/\n/g);
			if ($.browser.name === 'msie' && endsWith(currentText, '\n')) {
				//todo test for ie > 8
				allLines.push('');
			}
			return allLines;
		}
		function getLinesCount() {
			return getLines().length;
		}
		function getLastLine() {
			var allLines = getLines();
			return allLines[allLines.length - 1];
		}

		//speed interval
		var si = settings.speedInterval;
		//last correct date
		var lcd;
		//times
		var tms = [];
		//speed callback
		var scb = settings.speedTrackCallback;
		//finish callback
		var fcb = settings.finishCallback;
		function clearSpeed() {
			lcd = null;
			tms.length = 0;
		}
		var millisInMinute = 1000 * 60;
		var startTime = null;

		//error count
		var ec = 0;
		var eh = settings.errorCallback;

		var isError = false;
		var keyPress = function(e) {
			if (!startTime) {
				startTime = new Date().getTime();
			}
			if (e.keyCode === 8 || e.keyCode === 13) {
				return;
			}

			//a letter has been typed
			var lastTypedLine = getLastLine();
			var currentTypingPosition = lastTypedLine.length;
			if (!originalTexts[currentLinePosition]) {
				return;
			}
			if (currentTypingPosition >= originalTexts[currentLinePosition].length - 1) {
				//at the end of the line
				if (e.which === 32) {
					//trigger enter event
					e.preventDefault();
					e = new jQuery.Event('keydown');
					e.which = e.keyCode = 13;
					$(textarea).trigger(e);

					$(textarea).val($(textarea).val() + '\n');
					return;
				}
			}
			if (currentTypingPosition > originalTexts[currentLinePosition].length - 1) {
				increaseErrorCount();
				return;
			}
			if (originalTexts[currentLinePosition].substring(0, currentTypingPosition + 1) === lastTypedLine + String.fromCharCode(e.which)) {
				//a correct letter is about to be pressed
				isError = false;
				drawTextBackground(currentLinePosition, currentTypingPosition);
				drawCursor(currentLinePosition, currentTypingPosition + 1);
				if (scb) {
					//correct date
					var cd = new Date().getTime();
					if (lcd) {
						//recording current time
						var t = cd - lcd;
						tms.push(t);
						if (tms.length === si) {
							//calculate average and call speed feedback
							var tot = 0;
							$.each(tms, function(i, val) {
								tot += val;
							});
							//average speed of typing one character
							var avg = tot / (tms.length);

							var speed = millisInMinute / avg;
							scb.call(this, parseInt(speed));

							tms.length = 0;
						}
					}

					lcd = cd;
				}
				//check if the whole text is typed
				//at this point we are sure that the typed symbol was correct
				//if we're at the end of last line - fire 'finishedcallback'
				if (fcb) {
					if (!isError && originalTexts.length - 1 === currentLinePosition && originalTexts[originalTexts.length - 1].length - 2/*substracting last whitespace*/ === currentTypingPosition) {
						var time = new Date().getTime() - startTime;
						var overallSpeed = (totalCharacters / time) * millisInMinute;
						fcb.call(this, parseInt(overallSpeed));
						//append last typed char to the textarea before disabling it
						$(textarea).val($(textarea).val() + String.fromCharCode(e.which));
						$(textarea).attr('disabled', true);
						$(textarea).unbind('keypress');
						$(textarea).unbind('keydown');
						$(textarea).unbind('keyup');
					}
				}
			} else {
				highlightError(currentLinePosition, currentTypingPosition);
			}
		};
		var keyDown = function(e) {
			//skip arrow keys
			if($.inArray(e.keyCode, [37, 38, 39, 40]) !== -1){
				e.preventDefault();
				return;
			}
			//disable end key, allow only shift+end combo
			if(e.keyCode === 36 && !e.shiftKey){
			    e.preventDefault();
			    return;
			}
			if (e.keyCode === 8) {
				//backspace - unstyle last styled character
				var lastLine = getLastLine();
				var currentTypingPosition = lastLine.length;
				if (currentTypingPosition > 0) {
					if (!originalTexts[currentLinePosition]) {
						return;
					}

					if (currentTypingPosition < originalTexts[currentLinePosition].length) {
						clearTextBackground(currentLinePosition, currentTypingPosition);
					}
					if (currentTypingPosition > 1) {
						if (currentTypingPosition - 1 < originalTexts[currentLinePosition].length) {
							clearTextBackground(currentLinePosition, currentTypingPosition - 1);
							if (originalTexts[currentLinePosition].substring(0, currentTypingPosition - 1) === lastLine.substring(0, currentTypingPosition - 1)) {
								drawCursor(currentLinePosition, currentTypingPosition - 1);
							} else {
								highlightError(currentLinePosition, currentTypingPosition - 2);
							}
						}
					} else {
						drawCursor(currentLinePosition, 0);
					}
				} else {
					//we may have jumped to a previous line
					//Remove cursor from previous line
					if (currentLinePosition === 0) {
						//we're at the beginning of the first line
						return;
					}
					if(! originalTexts[currentLinePosition]){
						return;
					}

					clearTextBackground(currentLinePosition, 0);
					currentLinePosition--;
					var currentTypedLine = getLines()[currentLinePosition];
					currentTypingPosition = currentTypedLine.length + 1; //adding one for space at the end
					if (checkInput(currentTypedLine, currentLinePosition, currentTypingPosition)) {
						drawCursor(currentLinePosition, currentTypingPosition - 1);
					} else {
						highlightError(currentLinePosition, currentTypingPosition - 2);
					}
				}
			} else if (e.keyCode === 13) {
				//enter
				//validate current line
				var lastLine = getLastLine();
				if ($.trim(originalTexts[currentLinePosition]) !== lastLine) {
					highlightError(currentLinePosition, lastLine.length);
					e.preventDefault();
					return;
				} else {
					drawTextBackground(currentLinePosition, lastLine.length);
				}
				currentLinePosition++;
				drawCursor(currentLinePosition, 0);
			}
		};
		var keyUp = function(e){
			//var t1 = new Date().getTime();
			//clear backgrounds of of all characters which are beyond current typing point
			var lastLine = getLastLine();
			var currentTypingPosition = lastLine.length;
			if(currentTypingPosition >= lineLetters[currentLinePosition].length){
				return;
			}
			//adding one for cursor
			for(var i = currentTypingPosition + 1; i < lineLetters[currentLinePosition].length; i++){
				clearTextBackground(currentLinePosition, i);
			}
			//we don't want to replace errors with cursor
			if ((e.ctrlKey || e.shiftKey || e.keyCode === 46) && checkInput(lastLine, currentLinePosition, currentTypingPosition)) {
				drawCursor(currentLinePosition, currentTypingPosition);
			}
			//console.log('KeyUp in: ' + (new Date().getTime() - t1));
		};

		$(textarea).keypress(keyPress);
		$(textarea).keydown(keyDown);
		$(textarea).keyup(keyUp);

		function moveToEnd() {
			var text = $(textarea).val();
			$(textarea).focus().val('').val(text);
		}
		$(textarea).mouseup(moveToEnd);

		/**
		 * Checks if last line was correct up to currentTypingPosition
		 */
		function checkInput(lastLine, currentLinePosition, currentTypingPosition){
			return $.trim(originalTexts[currentLinePosition]).substring(0, currentTypingPosition - 1) === $.trim(lastLine).substring(0, currentTypingPosition - 1);
		}
		$(textarea).focus();
		drawCursor(0, 0);

		//resulting object
		var res = {};
		res.restart = function(){
			$(textarea).unbind('keypress');
			$(textarea).unbind('keydown');
			$(textarea).unbind('keyup');
			//clear backgrounds of all texts
			$.each(lineLetters, function(i, line){
				$.each(line, function(j, letter){
					letter.css('background-color', whiteColor);
				});
			});
			drawCursor(0, 0);

			currentLinePosition = 0;

			$(textarea).val('').attr('disabled', false).focus();

			ec = 0;

			if (eh) {
				eh.call(this, ec);
			}

			isError = false;

			if (scb) {
				scb.call(this, 0);
			}
			clearSpeed();
			startTime = null;

			$(textarea).keypress(keyPress);
			$(textarea).keydown(keyDown);
			$(textarea).keyup(keyUp);
		};

		return res;
	};
}(jQuery));<|endoftext|>


<commit_before>define([
	'goo/statemachine/actions/Action'
],
/** @lends */
function(
	Action
) {
	"use strict";

	function KeyPressedAction(/*id, settings*/) {
		Action.apply(this, arguments);

		this.everyFrame = true;
		this.keyIsDown = false;
		this.eventListenerDown = function(event) {
			if (event.which === +this.key) {
				this.keyIsDown = true;
			}
		}.bind(this);
		this.eventListenerUp = function(event) {
			if (event.which === +this.key) {
				document.removeEventListener('keydown', this.eventListenerUp);
				this.keyIsDown = false;
			}
		}.bind(this);
	}

	KeyPressedAction.prototype = Object.create(Action.prototype);
	KeyPressedAction.prototype.constructor = KeyPressedAction;

	KeyPressedAction.external = {
		name: 'Key Pressed',
		description: 'Listens for a key press and performs a transition. Works over transition boundaries.',
		canTransition: true,
		parameters: [{
			name: 'Key',
			key: 'key',
			type: 'key',
			description: 'Key to listen for'
		}],
		transitions: [{
			key: 'keydown',
			name: 'Key pressed',
			description: 'State to transition to when the key is pressed'
		}]
	};

	KeyPressedAction.prototype._setup = function() {
		document.addEventListener('keydown', this.eventListenerDown);
		document.addEventListener('keyup', this.eventListenerUp);
	};

	KeyPressedAction.prototype._run = function(fsm) {
		if (this.keyIsDown) {
			fsm.send(this.transitions.keydown);
		}
	};

	KeyPressedAction.prototype.exit = function() {
		document.removeEventListener('keydown', this.eventListenerDown);
	};

	return KeyPressedAction;
});<commit_msg>Fixed keypressedaction<commit_after>define([
	'goo/statemachine/actions/Action',
	'goo/statemachine/FSMUtil'
],
/** @lends */
function(
	Action,
	FSMUtil
) {
	"use strict";

	function KeyPressedAction(/*id, settings*/) {
		Action.apply(this, arguments);

		this.everyFrame = true;
		this.keyIsDown = false;
		this.eventListenerDown = function(event) {
			if (event.which === +this.key) {
				this.keyIsDown = true;
			}
		}.bind(this);
		this.eventListenerUp = function(event) {
			if (event.which === +this.key) {
				document.removeEventListener('keydown', this.eventListenerUp);
				this.keyIsDown = false;
			}
		}.bind(this);
	}

	KeyPressedAction.prototype = Object.create(Action.prototype);
	KeyPressedAction.prototype.constructor = KeyPressedAction;

	KeyPressedAction.external = {
		name: 'Key Pressed',
		description: 'Listens for a key press and performs a transition. Works over transition boundaries.',
		canTransition: true,
		parameters: [{
			name: 'Key',
			key: 'key',
			type: 'key',
			description: 'Key to listen for'
		}],
		transitions: [{
			key: 'keydown',
			name: 'Key pressed',
			description: 'State to transition to when the key is pressed'
		}]
	};

	KeyPressedAction.prototype.configure = function (settings) {
		this.key = settings.key ? FSMUtil.getKey(settings.key) : null;
		this.transitions = { keydown: settings.transitions.keydown };
	};

	KeyPressedAction.prototype._setup = function() {
		document.addEventListener('keydown', this.eventListenerDown);
		document.addEventListener('keyup', this.eventListenerUp);
	};

	KeyPressedAction.prototype._run = function(fsm) {
		if (this.keyIsDown) {
			fsm.send(this.transitions.keydown);
		}
	};

	KeyPressedAction.prototype.exit = function() {
		document.removeEventListener('keydown', this.eventListenerDown);
	};

	return KeyPressedAction;
});<|endoftext|>