<commit_before>import path from 'path';

import HtmlWebpackPlugin from 'html-webpack-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';

module.exports = {
  context: path.resolve(__dirname, 'src'),
  entry: 'app.js',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.js'
  },
  devtool: 'source-map',
  resolve: {
    modules: [
      path.resolve(__dirname, 'src'),
      path.resolve(__dirname, 'node_modules')
    ],
    extensions: [
      '.js', '.json', // Defaults
      '.jsx'
    ]
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/, // Match both .js and .jsx.
        exclude: /node_modules/,
        use: 'babel-loader'
      },
      {
        test: /\.scss$/,
        loader: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          loader: [ 'css-loader', 'sass-loader' ]
        })
      },
      {
        test: /\.(png|jpg|ico)/,
        use: 'url-loader?limit=10000'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      favicon: 'img/favicon.ico',
      template: 'index.html',
    }),

    new ExtractTextPlugin('style.css')
  ]
};
<commit_msg>Enable automatic webpage reloading<commit_after>import path from 'path';

import HtmlWebpackPlugin from 'html-webpack-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';

module.exports = {
  context: path.resolve(__dirname, 'src'),
  entry: 'app.js',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.js'
  },
  devtool: 'source-map',
  devServer: {
    // Enables automatic reloading when files are changed.
    //
    // As of February 10, 2017, Webpack's docs mention that: "By default the
    // application will be served with inline mode enabled." This doesn't seem
    // to be the case, as without the inline option specified, there is no auto
    // reloading.
    //
    // Reference: https://webpack.js.org/configuration/dev-server/#devserver-inline-cli-only
    inline: true
  },
  resolve: {
    modules: [
      path.resolve(__dirname, 'src'),
      path.resolve(__dirname, 'node_modules')
    ],
    extensions: [
      '.js', '.json', // Defaults
      '.jsx'
    ]
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/, // Match both .js and .jsx.
        exclude: /node_modules/,
        use: 'babel-loader'
      },
      {
        test: /\.scss$/,
        loader: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          loader: [ 'css-loader', 'sass-loader' ]
        })
      },
      {
        test: /\.(png|jpg|ico)/,
        use: 'url-loader?limit=10000'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      favicon: 'img/favicon.ico',
      template: 'index.html',
    }),

    new ExtractTextPlugin('style.css')
  ]
};
<|endoftext|>


<commit_before>'use strict';
import Webpack from 'webpack';
import Merge from 'webpack-merge';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import HTMLWebpackPlugin from 'html-webpack-plugin';
import DotEnv from 'dotenv-webpack';
import Path from 'path';
import PostCSS from './postcss.config';

// const appEnv = (process.env.NODE_ENV || 'dev');
const isProdEnv = (process.env.NODE_ENV === 'production');
  console.log(`Node Environment:\t${process.env.NODE_ENV}`);

const VENDOR_LIBS = [
  'body-parser',
  'bson',
  'cloudinary',
  'jquery',
  'lodash',
  'react',
  'react-dom',
  'react-motion',
  'react-redux',
  'react-router',
  'react-router-redux',
  'redux',
  'redux-thunk',
  'request',
  'uuid'
];

const BASE_CONFIG = {
  entry: {
    patch: 'react-hot-loader/patch',
    hmr: 'webpack/hot/only-dev-server',
    bundle: Path.resolve(__dirname, 'src/App'),
    vendor: VENDOR_LIBS
  },
  output: {
    path: Path.join(__dirname, 'dist'),
    filename: '[name].[hash].js'
    // publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/i,
        exclude: /(node_modules|bower_components)/,
        use: 'babel'
      }, {
        test: /\.css$/i,
        loader: ExtractTextPlugin.extract({
          fallback: 'style',
          use: 'css'
        })
      }, {
        test: /\.(scss|sass)$/i,
        loaders: ExtractTextPlugin.extract({
          fallback: 'style',
          use: ['css', 'postcss', 'sass']
        })
        // [
        //   'style',
        //   'css',
        //   'autoprefixer?browsers=last 3 versions',
        //   'sass?outputStyle=expanded'
        // ]
      }, {
        test: /\.json$/i,
        use: 'json'
      }, {
        test: /\.(bmp|gif|jpe?g|png|svg|ttif)$/i,
        use: [
          // {
          //   loader: 'url',
          //   options: { limit: 40000 }
          // },
          'file?name=/images/[name].[ext]',
          'image-webpack'
        ]
      }, {
        test: /\.(pdf|doc[mstx]?|ppt[mstx]?|od[fpst])$/i,
        use: 'file?name=/docs/[name].[ext]'
      }
    ]
  },
  plugins: [
    // Configure and read in local environment variables:
    new DotEnv({
      path: './.env'
    }),
    new Webpack.optimize.CommonsChunkPlugin({
      names: ['vendor', 'manifest'],
      minChunks: Infinity
    }),
    new Webpack.DefinePlugin({
      // 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')

      // 'process.env': {
      //   NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      // }
    }),
    new Webpack.LoaderOptionsPlugin({
      minimize: true,
      debug: false,
      options: {
        postcss: { PostCSS }
      }
    }),
    new ExtractTextPlugin('styles.css'),
    new HTMLWebpackPlugin({
      template: 'assets/index.html'
    })
  ],
  node: {
    console: false,
    fs: 'empty',
    net: 'empty',
    tls: 'empty'
  },
  cache: true,
  watch: true,
  devtool: `${isProdEnv ? 'cheap-eval' : 'inline'}-source-map`,
  resolve: {
    extensions: ['.js', '.jsx']
  },
  resolveLoader: {
    moduleExtensions: ['-loader']
  }
};

const DEV_SERVER = {
  plugins: [
    new Webpack.HotModuleReplacementPlugin(),
    new Webpack.NamedModulesPlugin()
  ],
  devServer: {
    // contentBase: __dirname,
    historyApiFallback: true,
    host: 'localhost',
    hot: true,
    inline: true,
    noInfo: false,
    port: 3000
  },
  stats: {
    assets: true,
    children: false,
    chunks: false,
    colors: {
      bold: '\u001b[1m',
      cyan: '\u001b[1m\u001b[36m',
      green: '\u001b[1m\u001b[32m',
      magenta: '\u001b[1m\u001b[35m',
      red: '\u001b[1m\u001b[31m',
      yellow: '\u001b[1m\u001b[33m'
    },
    errorDetails: true,
    hash: false,
    modules: false,
    publicPath: false,
    reasons: true,
    timings: true,
    version: false,
    warnings: true
  }
};

const AGGREGATE_CONFIG = isProdEnv
  ? BASE_CONFIG
  : Merge(BASE_CONFIG, DEV_SERVER);

export default AGGREGATE_CONFIG;

//     new Webpack.optimize.DedupePlugin(),
//     new Webpack.optimize.UglifyJsPlugin({
//       compress: { warnings: false },
//       output: { comments: false },
//       sourceMap: false
//     })
<commit_msg>config (Webpack): Include 'react-hot-loader/path' as first entry point<commit_after>'use strict';
import Webpack from 'webpack';
import Merge from 'webpack-merge';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import HTMLWebpackPlugin from 'html-webpack-plugin';
import DotEnv from 'dotenv-webpack';
import Path from 'path';
import PostCSS from './postcss.config';

// const appEnv = (process.env.NODE_ENV || 'dev');
const isProdEnv = (process.env.NODE_ENV === 'production');
  console.log(`Node Environment:\t${process.env.NODE_ENV}`);

const VENDOR_LIBS = [
  'body-parser',
  'bson',
  'cloudinary',
  'jquery',
  'lodash',
  'react',
  'react-dom',
  'react-motion',
  'react-redux',
  'react-router',
  'react-router-redux',
  'redux',
  'redux-thunk',
  'request',
  'uuid'
];

// 'react-hot-loader/patch', 'webpack-dev-server/client?http://localhost:3000', 'webpack/hot/only-dev-server'

// patch: 'react-hot-loader/patch',
// client: 'webpack-dev-server/client?http://localhost:3000',
// hmr: 'webpack/hot/only-dev-server',

// bundle: Path.resolve(__dirname, 'src/App'),
// context: Path.resolve(__dirname, 'src'),

const BASE_CONFIG = {
  entry: {
    patch: 'react-hot-loader/patch',
    hmr: 'webpack/hot/only-dev-server',
    bundle: Path.resolve(__dirname, 'src/HMR'),
    vendor: VENDOR_LIBS
  },
  output: {
    path: Path.join(__dirname, 'dist'),
    filename: '[name].[hash].js',
    // publicPath: '/' // Server-Relative
    publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/i,
        include: Path.resolve(__dirname, 'src'),
        exclude: /(node_modules|bower_components)/,
        use: 'babel'
      }, {
        test: /\.css$/i,
        loader: ExtractTextPlugin.extract({
          fallback: 'style',
          use: 'css'
        })
      }, {
        test: /\.(scss|sass)$/i,
        loaders: ExtractTextPlugin.extract({
          fallback: 'style',
          use: ['css', 'postcss', 'sass?outputStyle=expanded']
        })
      }, {
        test: /\.json$/i,
        use: 'json'
      }, {
        test: /\.(bmp|gif|jpe?g|png|svg|ttif)$/i,
        use: [
          // {
          //   loader: 'url',
          //   options: { limit: 40000 }
          // },
          'file?name=/images/[name].[ext]',
          'image-webpack'
        ]
      }, {
        test: /\.(pdf|doc[mstx]?|ppt[mstx]?|od[fpst])$/i,
        use: 'file?name=/docs/[name].[ext]'
      }
    ]
  },
  plugins: [
    // Configure and read in local environment variables:
    new DotEnv({
      path: './.env'
    }),
    new Webpack.optimize.CommonsChunkPlugin({
      names: ['vendor', 'manifest'],
      minChunks: Infinity
    }),
    new Webpack.DefinePlugin({
      // 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')

      // 'process.env': {
      //   NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      // }
    }),
    new Webpack.LoaderOptionsPlugin({
      minimize: true,
      debug: false,
      options: {
        postcss: { PostCSS }
      }
    }),
    new ExtractTextPlugin('styles.css'),
    new HTMLWebpackPlugin({
      template: 'assets/index.html'
    })
  ],
  node: {
    console: false,
    fs: 'empty',
    net: 'empty',
    tls: 'empty'
  },
  cache: true,
  watch: true,
  devtool: `${isProdEnv ? 'cheap-eval' : 'inline'}-source-map`,
  resolve: {
    extensions: ['.js', '.jsx']
  },
  resolveLoader: {
    moduleExtensions: ['-loader']
  }
};

const DEV_SERVER = {
  plugins: [
    new Webpack.HotModuleReplacementPlugin(),
    new Webpack.NamedModulesPlugin()
  ],
  devServer: {
    contentBase: Path.resolve(__dirname, 'dist'),
    historyApiFallback: true,
    host: 'localhost',
    hot: true,
    inline: true,
    noInfo: false,
    port: 3000,
    publicPath: '/'
  },
  stats: {
    assets: true,
    children: false,
    chunks: false,
    colors: {
      bold: '\u001b[1m',
      cyan: '\u001b[1m\u001b[36m',
      green: '\u001b[1m\u001b[32m',
      magenta: '\u001b[1m\u001b[35m',
      red: '\u001b[1m\u001b[31m',
      yellow: '\u001b[1m\u001b[33m'
    },
    errorDetails: true,
    hash: false,
    modules: false,
    publicPath: false,
    reasons: true,
    timings: true,
    version: false,
    warnings: true
  }
};

const AGGREGATE_CONFIG = isProdEnv
  ? BASE_CONFIG
  : Merge(BASE_CONFIG, DEV_SERVER);

export default AGGREGATE_CONFIG;

//     new Webpack.optimize.DedupePlugin(),
//     new Webpack.optimize.UglifyJsPlugin({
//       compress: { warnings: false },
//       output: { comments: false },
//       sourceMap: false
//     })
<|endoftext|>


<commit_before>const _ = require('lodash');
const path = require('path');
const webpack = require('webpack');
const config = require('./config');
const appPath = path.join(__dirname, '..', '..');

const wds = {
  hostname: process.env.WATCH_HOSTNAME || 'localhost',
  port: process.env.WATCH_PORT || 8080,
};
const proxy = {
  hostname: process.env.HOSTNAME || 'localhost',
  port: process.env.PORT || 8000,
};

config.entry.unshift(
  'webpack-dev-server/client?http://' + wds.hostname + ':' + wds.port,
  'webpack/hot/only-dev-server'
);

config.devServer = {
  publicPath: '/assets/',
  contentBase: path.resolve(path.join(appPath, 'static')),
  hot: true,
  inline: false,
  lazy: false,
  quiet: true,
  noInfo: true,
  headers: {
    'Access-Control-Allow-Origin': '*',
  },
  proxy: {
    '**': 'http://' + proxy.hostname + ':' + proxy.port,
  },
  stats: {
    colors: true,
  },
  host: wds.hostname,
  port: wds.port,
};

module.exports = _.mergeWith(config, {
  cache: true,
  devtool: 'eval',
  output: {
    filename: 'client.js',
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ],
}, function(objValue, srcValue) {
  if (_.isArray(objValue)) {
    return objValue.concat(srcValue);
  }
});
<commit_msg>Add `react-hot-loader/patch` to webpack config<commit_after>const _ = require('lodash');
const path = require('path');
const webpack = require('webpack');
const config = require('./config');
const appPath = path.join(__dirname, '..', '..');

const wds = {
  hostname: process.env.WATCH_HOSTNAME || 'localhost',
  port: process.env.WATCH_PORT || 8080,
};
const proxy = {
  hostname: process.env.HOSTNAME || 'localhost',
  port: process.env.PORT || 8000,
};

config.entry.unshift(
  'react-hot-loader/patch',
  'webpack-dev-server/client?http://' + wds.hostname + ':' + wds.port,
  'webpack/hot/only-dev-server'
);

config.devServer = {
  publicPath: '/assets/',
  contentBase: path.resolve(path.join(appPath, 'static')),
  hot: true,
  inline: false,
  lazy: false,
  quiet: true,
  noInfo: true,
  headers: {
    'Access-Control-Allow-Origin': '*',
  },
  proxy: {
    '**': 'http://' + proxy.hostname + ':' + proxy.port,
  },
  stats: {
    colors: true,
  },
  host: wds.hostname,
  port: wds.port,
};

module.exports = _.mergeWith(config, {
  cache: true,
  devtool: 'eval',
  output: {
    filename: 'client.js',
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ],
}, function(objValue, srcValue) {
  if (_.isArray(objValue)) {
    return objValue.concat(srcValue);
  }
});
<|endoftext|>


<commit_before>let path = require('path');
let serverConfig = require('./server/config');

module.exports = {
  devtool: 'inline-source-map',
  module: {
    loaders: serverConfig.module.loaders,
    noParse: [
      /\.min\.js/,
      /sinon/,
    ],
  },
  resolve: {
    modulesDirectories: serverConfig.resolve.modulesDirectories,
    alias: {
      src: path.dirname(__dirname) + '/src/',
      sinon: 'sinon/pkg/sinon',
    },
  },
  plugins: serverConfig.plugins,
  externals: {
    'react/addons': true,
    'react/lib/ExecutionEnvironment': true,
    'react/lib/ReactContext': true,
  },
};
<commit_msg>Represent `noParse` option as `RegExp` instead of `[RegExp]`<commit_after>let path = require('path');
let serverConfig = require('./server/config');

module.exports = {
  devtool: 'inline-source-map',
  module: {
    loaders: serverConfig.module.loaders,
    noParse: /sinon|\.min\.js/,
  },
  resolve: {
    modulesDirectories: serverConfig.resolve.modulesDirectories,
    alias: {
      src: path.dirname(__dirname) + '/src/',
      sinon: 'sinon/pkg/sinon',
    },
  },
  plugins: serverConfig.plugins,
  externals: {
    'react/addons': true,
    'react/lib/ExecutionEnvironment': true,
    'react/lib/ReactContext': true,
  },
};
<|endoftext|>


<commit_before>/****************************************************************************************
 * Webpack test configuration file
 * Using the current configuration we are able to bundle all the files we need for our
 * unit testings.
 ***************************************************************************************/

'use strict';

/**
 * Dependencies:
 * path: node library for resolving full path names
 * webpack: for use in our testings
 */
const path = require('path');
const webpack = require('webpack');

module.exports = {
    /**
     * Choose a developer tool to enhance debugging. inline-source-map - A SourceMap is added as DataUrl to the JavaScript file.
     */
    devtool: 'inline-source-map',

    /**
     * Set webpack loaders and preloaders for typescript, angular2, SASS and HTML,
     * so that webpack will be able to process and bundle them.
     */
    module: {

        rules: [
            {
                test: /.ts$/,
                exclude: /node_modules/,
                use: [
                    'awesome-typescript-loader',
                    'angular2-template-loader',
                    'angular2-router-loader'
                ]
            },
            { test: /\.html$/, loader: "html-loader" },
            {
                test: /.scss$/,
                use: ['style-loader', 'css-loader', 'sass-loader'],
                include: path.resolve(__dirname, '../')
            },

        ]
    },

    /**
     * This section lets Webpack know which types of file extensions it should be loading.
     */
    resolve: {
        extensions: ['.js', '.ts'],
        modules: [
            path.resolve('.', 'src'),
            'node_modules'
        ]
    },
};
<commit_msg>Added plugin to solve system-js warning (Angular workaround for webpack)<commit_after>/****************************************************************************************
 * Webpack test configuration file
 * Using the current configuration we are able to bundle all the files we need for our
 * unit testings.
 ***************************************************************************************/

'use strict';

/**
 * Dependencies:
 * path: node library for resolving full path names
 * webpack: for use in our testings
 */
const path = require('path');
const webpack = require('webpack');
const helpers = require('./helpers');
module.exports = {
    /**
     * Choose a developer tool to enhance debugging. inline-source-map - A SourceMap is added as DataUrl to the JavaScript file.
     */
    devtool: 'inline-source-map',

    /**
     * Set webpack loaders and preloaders for typescript, angular2, SASS and HTML,
     * so that webpack will be able to process and bundle them.
     */
    module: {

        rules: [
            {
                test: /.ts$/,
                exclude: /node_modules/,
                use: [
                    'awesome-typescript-loader',
                    'angular2-template-loader',
                    'angular2-router-loader'
                ]
            },
            { test: /\.html$/, loader: "html-loader" },
            {
                test: /.scss$/,
                use: ['style-loader', 'css-loader', 'sass-loader'],
                include: path.resolve(__dirname, '../')
            },

        ]
    },

    plugins: [
        // Workaround for angular/angular#11580
        new webpack.ContextReplacementPlugin(
            // The (\\|\/) piece accounts for path separators in *nix and Windows
            ///angular(\\|\/)core(\\|\/)@angular/,
            /@angular(\\|\/)core(\\|\/)/,
            helpers.root('./src'), // location of your src
            {} // a map of your routes
        ),
    ],

    /**
     * This section lets Webpack know which types of file extensions it should be loading.
     */
    resolve: {
        extensions: ['.js', '.ts'],
        modules: [
            path.resolve('.', 'src'),
            'node_modules'
        ]
    },
};
<|endoftext|>


<commit_before>var Glosser = Glosser || {};

Glosser.morphemefinder = function(unparsedword, corpusname, callback) {
  unparsedword = "@" + unparsedword + "@";
  var potentialParsePromise = '';
  $.ajax({
    type : 'GET',
    url : "https://ilanguage.iriscouch.com/" + corpusname
        + "/_design/get_precedence_rules_from_morphemes/_view/precedence_rules?group=true",
    success : function(rules) {
      // console.log(rules);
      rules = JSON.parse(rules);

      /*
       * Reduce the rules such that rules which are found in multiple source
       * words are only used/included once.
       */
      var reducedRules = _.chain(rules.rows).groupBy(function(rule) {
        return rule.key.x + "-" + rule.key.y;
      }).value();

      /*
       * Find the rules which match in local precedence in the unparsedword
       */
      var matchedRules = [];
      for ( var r in reducedRules) {
        if (unparsedword.indexOf(r.replace(/-/, "")) >= 0) {
          matchedRules.push({
            r : reducedRules[r]
          })
        }
      }

      /*
       * Attempt to find the longest template which the matching rules can
       * generate from start to end
       */
      var prefixtemplate = [];
      prefixtemplate.push("@");
      for ( var i = 0; i < 10; i++) {
        console.log(prefixtemplate[i]);
        if (prefixtemplate[i] == undefined)
          break;
        for ( var j in matchedRules) {
          if (prefixtemplate[i] == matchedRules[j].r[0].key.x) {
            console.log(matchedRules[j].r[0].key.x);
            if (prefixtemplate[i + 1]) { // ambiguity (two potential following
                                          // morphemes)
              prefixtemplate.pop();
              break;
            } else {
              prefixtemplate[i + 1] = matchedRules[j].r[0].key.y;
            }
          }
        }
      }

      /*
       * If the prefix template hit ambiguity in the middle, try from the suffix
       * in until it hits ambiguity
       */
      var suffixtemplate = [];
      if (prefixtemplate[prefixtemplate.length - 1] != "@"
          || prefixtemplate.length == 1) {
        // Suffix:
        suffixtemplate.push("@")
        for ( var i = 0; i < 10; i++) {
          console.log(suffixtemplate[i]);
          if (suffixtemplate[i] == undefined)
            break;
          for ( var j in matchedRules) {
            if (suffixtemplate[i] == matchedRules[j].r[0].key.y) {
              console.log(matchedRules[j].r[0].key.y);
              if (suffixtemplate[i + 1]) { // ambiguity (two potential
                                            // following morphemes)
                suffixtemplate.pop();
                break;
              } else {
                suffixtemplate[i + 1] = matchedRules[j].r[0].key.x;
              }
            }
          }
        }
      }
      /*
       * Combine prefix and suffix templates into one regular expression which
       * can be tested against the word to find a potential parse
       */
      var template = [];
      template = prefixtemplate.concat(suffixtemplate.reverse())
      for ( var slot in template) {
        template[slot] = "(" + template[slot] + ")";
      }
      var regex = new RegExp(template.join("(.*)"), "");
      console.log(unparsedword.replace(regex, "$1-$2-$3-$4-$5-$6-$7-$8-$9"));
      potentialParsePromise = unparsedword.replace(regex,
          "$1-$2-$3-$4-$5-$6-$7-$8-$9").replace(/\$[0-9]/g, "").replace(/@/g,
          "").replace(/--+/g, "-").replace(/^-/, "").replace(/-$/, "")
      /* "hall-pa-nay-wan" */
      console.log("Potential parse of " + unparsedword.replace(/@/g, "")
          + " is " + potentialParsePromise);
      if (typeof callback == "function") {
        callback(potentialParsePromise);
      }

    },// end successful login\
    error : function(data) {
      console.log(data);
    },
    dataType : ""
  });

  return potentialParsePromise;
}

/**
 * Takes as a parameters an array of rules which came from CouchDB precedence rule query.
 * Example Rule: {"key":{"x":"@","relation":"preceeds","y":"aqtu","context":"aqtu-nay-wa-n"},"value":2}
 */
Glosser.generateForceDirectedRulesJsonForD3 = function(rules) {

  /*
   * Cycle through the precedence rules, convert them into graph edges with the morpheme index in the morpheme array as the source/target values
   */
  morphemeLinks = [];
  morphemes = [];
  for ( var i in rules) {
    var xpos = morphemes.indexOf(rules[i].key.x);
    if (xpos < 0) {
      morphemes.push(rules[i].key.x);
      xpos = morphemes.length - 1;
    }
    var ypos = morphemes.indexOf(rules[i].key.y);
    if (ypos < 0) {
      morphemes.push(rules[i].key.y);
      ypos = morphemes.length - 1;
    }
    if (rules[i].key.y != "@") {
      morphemeLinks.push({
        source : xpos,
        target : ypos,
        value : 1 //TODO use the context counting to get a weight measure
      });
    }
  }

  /*
   * Build the morphemes into nodes and color them by their morpheme length, could be a good measure of outliers
   */
  var morphemenodes = [];
  for (m in morphemes) {
    morphemenodes.push({
      name : morphemes[m],
      group : morphemes[m].length
    });
  }

  /*
   * Create the JSON required by D3
   */
  var rulesGraph = {};
  rulesGraph.links = morphemeLinks;
  rulesGraph.nodes = morphemenodes;

  return rulesGraph;
}

/*
 * Some sample D3 from the force-html.html example
 *
 */
Glosser.visualizeMorphemesAsForceDirectedGraph = function(){

  var width = 960,
      height = 500,
      radius = 6,
      fill = d3.scale.category20();

  var force = d3.layout.force()
      .charge(-120)
      .linkDistance(30)
      .size([width, height]);

  var vis = d3.select("body").append("div")
      .style("width", width + "px")
      .style("height", height + "px");

  var tooltip = null;

  d3.json("rules.json", function(json) {
    var link = vis.selectAll("div.link")
        .data(json.links)
      .enter().append("div")
        .attr("class", "link");

    var node = vis.selectAll("div.node")
        .data(json.nodes)
      .enter().append("div")
        .attr("class", "node")
        .style("background", function(d) { return fill(d.group); })
        .style("border-color", function(d) { return d3.rgb(fill(d.group)).darker(); })
      .on("mouseover", function(d) {
        tooltip = d3.select("body")
          .append("div")
          .style("position", "absolute")
          .style("z-index", "10")
          .style("visibility", "visible")
          .text(d.name)
        })
      .on("mouseout", function() {
          tooltip.style("visibility", "hidden");
        })
      .call(force.drag);

    force
        .nodes(json.nodes)
        .links(json.links)
        .on("tick", tick)
        .start();

    function tick() {
      node.style("left", function(d) { return (d.x = Math.max(radius, Math.min(width - radius, d.x))) + "px"; })
          .style("top", function(d) { return (d.y = Math.max(radius, Math.min(height - radius, d.y))) + "px"; });

      link.style("left", function(d) { return d.source.x + "px"; })
          .style("top", function(d) { return d.source.y + "px"; })
          .style("width", length)
          .style("-webkit-transform", transform)
          .style("-moz-transform", transform)
          .style("-ms-transform", transform)
          .style("-o-transform", transform)
          .style("transform", transform);
    }

    function transform(d) {
      return "rotate(" + Math.atan2(d.target.y - d.source.y, d.target.x - d.source.x) * 180 / Math.PI + "deg)";
    }

    function length(d) {
      var dx = d.target.x - d.source.x,
          dy = d.target.y - d.source.y;
      return Math.sqrt(dx * dx + dy * dy) + "px";
    }
  });

}<commit_msg>Morpheme finder now handles utterances with multiple words. Fixes #370<commit_after>var Glosser = Glosser || {};

/**
 * Takes in an utterance line and, based on our current set of precendence
 * rules, guesses what the morpheme line would be. The algorithm is
 * very conservative.
 *
 * @param {String} unparsedUtterance The raw utterance line.
 * @param {String} corpusname The name of the corpus where this utterance
 * occurs. We need this in order to compare it with rules from the same
 * corpus.
 * @param {Function} callback The optional callback that takes a single
 * String parameter containing the guessed morphemes line.
 */
Glosser.morphemefinder = function(unparsedUtterance, corpusname, callback) {
  var potentialParsePromise = '';

  // Get the rules from Couch
  // TODO Move this part into the replication code so that this function will work
  // offline
  $.ajax({
    type : 'GET',
    url : "https://ilanguage.iriscouch.com/" + corpusname
        + "/_design/get_precedence_rules_from_morphemes/_view/precedence_rules?group=true",
    success : function(rules) {
      // Parse the rules from JSON into an object
      rules = JSON.parse(rules);

      // Reduce the rules such that rules which are found in multiple source
      // words are only used/included once.
      var reducedRules = _.chain(rules.rows).groupBy(function(rule) {
        return rule.key.x + "-" + rule.key.y;
      }).value();

      // Divide the utterance line into words
      var unparsedWords = unparsedUtterance.trim().split(/ +/);

      var parsedWords = [];
      for (var word in unparsedWords) {
        // Add the start/end-of-word character to the word
        unparsedWords[word] = "@" + unparsedWords[word] + "@";

        // Find the rules which match in local precedence
        var matchedRules = [];
        for (var r in reducedRules) {
          if (unparsedWords[word].indexOf(r.replace(/-/, "")) >= 0) {
            matchedRules.push({
              r : reducedRules[r]
            })
          }
        }

        // Attempt to find the longest template which the matching rules can
        // generate from start to end
        var prefixtemplate = [];
        prefixtemplate.push("@");
        for (var i = 0; i < 10; i++) {
          if (prefixtemplate[i] == undefined) {
            break;
          }
          for (var j in matchedRules) {
            if (prefixtemplate[i] == matchedRules[j].r[0].key.x) {
              if (prefixtemplate[i + 1]) { // ambiguity (two potential following
                                            // morphemes)
                prefixtemplate.pop();
                break;
              } else {
                prefixtemplate[i + 1] = matchedRules[j].r[0].key.y;
              }
            }
          }
        }

        // If the prefix template hit ambiguity in the middle, try from the suffix
        // in until it hits ambiguity
        var suffixtemplate = [];
        if (prefixtemplate[prefixtemplate.length - 1] != "@" || prefixtemplate.length == 1) {
          // Suffix:
          suffixtemplate.push("@")
          for (var i = 0; i < 10; i++) {
            if (suffixtemplate[i] == undefined) {
              break;
            }
            for (var j in matchedRules) {
              if (suffixtemplate[i] == matchedRules[j].r[0].key.y) {
                if (suffixtemplate[i + 1]) { // ambiguity (two potential
                                              // following morphemes)
                  suffixtemplate.pop();
                  break;
                } else {
                  suffixtemplate[i + 1] = matchedRules[j].r[0].key.x;
                }
              }
            }
          }
        }

        // Combine prefix and suffix templates into one regular expression which
        // can be tested against the word to find a potential parse.
        // Regular expressions will look something like
        //    (@)(.*)(hall)(.*)(o)(.*)(wa)(.*)(n)(.*)(@)
        var template = [];
        template = prefixtemplate.concat(suffixtemplate.reverse())
        for (var slot in template) {
          template[slot] = "(" + template[slot] + ")";
        }
        var regex = new RegExp(template.join("(.*)"), "");

        // Use the regular expression to find a guessed morphemes line
        potentialParsePromise = unparsedWords[word]
            .replace(regex, "$1-$2-$3-$4-$5-$6-$7-$8-$9") // Use backreferences to parse into morphemes
            .replace(/\$[0-9]/g, "")// Remove any backreferences that weren't used
            .replace(/@/g, "")      // Remove the start/end-of-line symbol
            .replace(/--+/g, "-")   // Ensure that there is only ever one "-" in a row
            .replace(/^-/, "")      // Remove "-" at the start of the word
            .replace(/-$/, "");     // Remove "-" at the end of the word
        Utils.debug("Potential parse of " + unparsedWords[word].replace(/@/g, "")
            + " is " + potentialParsePromise);

        parsedWords.push(potentialParsePromise);
      }

      // Call the callback with the guessed morphemes line
      if (typeof callback == "function") {
        callback(parsedWords.join(" "));
      }

    },// end successful login
    dataType : ""
  });

  return potentialParsePromise;
}

/**
 * Takes as a parameters an array of rules which came from CouchDB precedence rule query.
 * Example Rule: {"key":{"x":"@","relation":"preceeds","y":"aqtu","context":"aqtu-nay-wa-n"},"value":2}
 */
Glosser.generateForceDirectedRulesJsonForD3 = function(rules) {

  /*
   * Cycle through the precedence rules, convert them into graph edges with the morpheme index in the morpheme array as the source/target values
   */
  morphemeLinks = [];
  morphemes = [];
  for ( var i in rules) {
    var xpos = morphemes.indexOf(rules[i].key.x);
    if (xpos < 0) {
      morphemes.push(rules[i].key.x);
      xpos = morphemes.length - 1;
    }
    var ypos = morphemes.indexOf(rules[i].key.y);
    if (ypos < 0) {
      morphemes.push(rules[i].key.y);
      ypos = morphemes.length - 1;
    }
    if (rules[i].key.y != "@") {
      morphemeLinks.push({
        source : xpos,
        target : ypos,
        value : 1 //TODO use the context counting to get a weight measure
      });
    }
  }

  /*
   * Build the morphemes into nodes and color them by their morpheme length, could be a good measure of outliers
   */
  var morphemenodes = [];
  for (m in morphemes) {
    morphemenodes.push({
      name : morphemes[m],
      group : morphemes[m].length
    });
  }

  /*
   * Create the JSON required by D3
   */
  var rulesGraph = {};
  rulesGraph.links = morphemeLinks;
  rulesGraph.nodes = morphemenodes;

  return rulesGraph;
}

/*
 * Some sample D3 from the force-html.html example
 *
 */
Glosser.visualizeMorphemesAsForceDirectedGraph = function(){

  var width = 960,
      height = 500,
      radius = 6,
      fill = d3.scale.category20();

  var force = d3.layout.force()
      .charge(-120)
      .linkDistance(30)
      .size([width, height]);

  var vis = d3.select("body").append("div")
      .style("width", width + "px")
      .style("height", height + "px");

  var tooltip = null;

  d3.json("rules.json", function(json) {
    var link = vis.selectAll("div.link")
        .data(json.links)
      .enter().append("div")
        .attr("class", "link");

    var node = vis.selectAll("div.node")
        .data(json.nodes)
      .enter().append("div")
        .attr("class", "node")
        .style("background", function(d) { return fill(d.group); })
        .style("border-color", function(d) { return d3.rgb(fill(d.group)).darker(); })
      .on("mouseover", function(d) {
        tooltip = d3.select("body")
          .append("div")
          .style("position", "absolute")
          .style("z-index", "10")
          .style("visibility", "visible")
          .text(d.name)
        })
      .on("mouseout", function() {
          tooltip.style("visibility", "hidden");
        })
      .call(force.drag);

    force
        .nodes(json.nodes)
        .links(json.links)
        .on("tick", tick)
        .start();

    function tick() {
      node.style("left", function(d) { return (d.x = Math.max(radius, Math.min(width - radius, d.x))) + "px"; })
          .style("top", function(d) { return (d.y = Math.max(radius, Math.min(height - radius, d.y))) + "px"; });

      link.style("left", function(d) { return d.source.x + "px"; })
          .style("top", function(d) { return d.source.y + "px"; })
          .style("width", length)
          .style("-webkit-transform", transform)
          .style("-moz-transform", transform)
          .style("-ms-transform", transform)
          .style("-o-transform", transform)
          .style("transform", transform);
    }

    function transform(d) {
      return "rotate(" + Math.atan2(d.target.y - d.source.y, d.target.x - d.source.x) * 180 / Math.PI + "deg)";
    }

    function length(d) {
      var dx = d.target.x - d.source.x,
          dy = d.target.y - d.source.y;
      return Math.sqrt(dx * dx + dy * dy) + "px";
    }
  });

}<|endoftext|>


<commit_before>var Glosser = Glosser || {};
Glosser.currentCorpusName = "";
Glosser.downloadPrecedenceRules = function(pouchname, glosserURL, callback){
  if(!glosserURL ||glosserURL == "default"){
    var couchConnection = app.get("corpus").get("couchConnection");
    var couchurl = OPrime.getCouchUrl(couchConnection);
    glosserURL = couchurl + "/_design/pages/_view/precedence_rules?group=true";
  }
  OPrime.makeCORSRequest({
    type : 'GET',
    url : glosserURL,
    success : function(rules) {
      localStorage.setItem(pouchname+"precendenceRules", JSON.stringify(rules.rows));

      // Reduce the rules such that rules which are found in multiple source
      // words are only used/included once.
      var reducedRules = _.chain(rules.rows).groupBy(function(rule) {
        return rule.key.x + "-" + rule.key.y;
      }).value();

      // Save the reduced precedence rules in localStorage
      localStorage.setItem(pouchname+"reducedRules", JSON.stringify(reducedRules));
      Glosser.currentCorpusName = pouchname;
      if(typeof callback == "function"){
        callback();
      }
    },
    error : function(e) {
      console.log("error getting precedence rules:", e);
    },
    dataType : ""
  });
};
/**
 * Takes in an utterance line and, based on our current set of precendence
 * rules, guesses what the morpheme line would be. The algorithm is
 * very conservative.
 *
 * @param {String} unparsedUtterance The raw utterance line.
 *
 * @return {String} The guessed morphemes line.
 */
Glosser.morphemefinder = function(unparsedUtterance) {
  var potentialParse = '';

  // Get the precedence rules from localStorage
  var rules = localStorage.getItem(Glosser.currentCorpusName+"reducedRules");

  var parsedWords = [];
  if (rules) {
    // Parse the rules from JSON into an object
    rules = JSON.parse(rules);

    // Divide the utterance line into words
    var unparsedWords = unparsedUtterance.trim().split(/ +/);

    for (var word in unparsedWords) {
      // Add the start/end-of-word character to the word
      unparsedWords[word] = "@" + unparsedWords[word] + "@";

      // Find the rules which match in local precedence
      var matchedRules = [];
      for (var r in rules) {
        if (unparsedWords[word].indexOf(r.replace(/-/, "")) >= 0) {
          matchedRules.push({
            r : rules[r]
          })
        }
      }

      // Attempt to find the longest template which the matching rules can
      // generate from start to end
      var prefixtemplate = [];
      prefixtemplate.push("@");
      for (var i = 0; i < 10; i++) {
        if (prefixtemplate[i] == undefined) {
          break;
        }
        for (var j in matchedRules) {
          if (prefixtemplate[i] == matchedRules[j].r[0].key.x) {
            if (prefixtemplate[i + 1]) { // ambiguity (two potential following
                                          // morphemes)
              prefixtemplate.pop();
              break;
            } else {
              prefixtemplate[i + 1] = matchedRules[j].r[0].key.y;
            }
          }
        }
      }

      // If the prefix template hit ambiguity in the middle, try from the suffix
      // in until it hits ambiguity
      var suffixtemplate = [];
      if (prefixtemplate[prefixtemplate.length - 1] != "@" || prefixtemplate.length == 1) {
        // Suffix:
        suffixtemplate.push("@")
        for (var i = 0; i < 10; i++) {
          if (suffixtemplate[i] == undefined) {
            break;
          }
          for (var j in matchedRules) {
            if (suffixtemplate[i] == matchedRules[j].r[0].key.y) {
              if (suffixtemplate[i + 1]) { // ambiguity (two potential
                                            // following morphemes)
                suffixtemplate.pop();
                break;
              } else {
                suffixtemplate[i + 1] = matchedRules[j].r[0].key.x;
              }
            }
          }
        }
      }

      // Combine prefix and suffix templates into one regular expression which
      // can be tested against the word to find a potential parse.
      // Regular expressions will look something like
      //    (@)(.*)(hall)(.*)(o)(.*)(wa)(.*)(n)(.*)(@)
      var template = [];
      template = prefixtemplate.concat(suffixtemplate.reverse())
      for (var slot in template) {
        template[slot] = "(" + template[slot] + ")";
      }
      var regex = new RegExp(template.join("(.*)"), "");

      // Use the regular expression to find a guessed morphemes line
      potentialParse = unparsedWords[word]
          .replace(regex, "$1-$2-$3-$4-$5-$6-$7-$8-$9") // Use backreferences to parse into morphemes
          .replace(/\$[0-9]/g, "")// Remove any backreferences that weren't used
          .replace(/@/g, "")      // Remove the start/end-of-line symbol
          .replace(/--+/g, "-")   // Ensure that there is only ever one "-" in a row
          .replace(/^-/, "")      // Remove "-" at the start of the word
          .replace(/-$/, "");     // Remove "-" at the end of the word
      if (OPrime.debugMode) OPrime.debug("Potential parse of " + unparsedWords[word].replace(/@/g, "")
          + " is " + potentialParse);

      parsedWords.push(potentialParse);
    }
  }

  return parsedWords.join(" ");
};
Glosser.toastedUserToSync = false;
Glosser.toastedUserToImport = 0;
Glosser.glossFinder = function(morphemesLine){
  //Guess a gloss
  var morphemeGroup = morphemesLine.split(/ +/);
  var glossGroups = [];
  if(! window.app.get("corpus")){
    return "";
  }
  if(! window.app.get("corpus").lexicon.get("lexiconNodes")){
    var corpusSize = 31; //TODO get corpus size another way. // app.get("corpus").datalists.models[app.get("corpus").datalists.models.length-1].get("datumIds").length;
    if(corpusSize > 30 && !Glosser.toastedUserToSync){
      Glosser.toastedUserToSync = true;
      window.appView.toastUser("You probably have enough data to train an autoglosser for your corpus.\n\nIf you sync your data with the team server then editing the morphemes will automatically run the auto glosser.","alert-success","Sync to train your auto-glosser:");
    }else{
      Glosser.toastedUserToImport ++;
      if(Glosser.toastedUserToImport % 10 == 1 && corpusSize < 30){
        window.appView.toastUser("You have roughly "+corpusSize+" datum saved in your pouch, if you have around 30 datum, then you have enough data to train an autoglosser for your corpus.","alert-info","AutoGlosser:");
      }
    }
    return "";
  }
  var lexiconNodes = window.app.get("corpus").lexicon.get("lexiconNodes");
  for (var group in morphemeGroup) {
    var morphemes = morphemeGroup[group].split("-");
    var glosses = [];
    for (var m in morphemes) {
      // Take the first gloss for this morpheme
      var matchingNode = _.max(lexiconNodes.where({morpheme: morphemes[m]}), function(node) { return node.get("value"); });
//      console.log(matchingNode);
      var gloss = "?";   // If there's no matching gloss, use question marks
      if (matchingNode) {
        gloss = matchingNode.get("gloss");
      }
      glosses.push(gloss);
    }

    glossGroups.push(glosses.join("-"));
  }

  // Replace the gloss line with the guessed glosses
  return glossGroups.join(" ");
};
/**
 * Takes as a parameters an array of rules which came from CouchDB precedence rule query.
 * Example Rule: {"key":{"x":"@","relation":"preceeds","y":"aqtu","context":"aqtu-nay-wa-n"},"value":2}
 */
Glosser.generateForceDirectedRulesJsonForD3 = function(rules, pouchname) {
  if(!pouchname){
    pouchname = Glosser.currentCorpusName;
  }
  if(!rules){
    rules = localStorage.getItem(pouchname+"precendenceRules");
    if(rules){
      rules = JSON.parse(rules);
    }
  }
  if(!rules ){
    return;
  }
  /*
   * Cycle through the precedence rules, convert them into graph edges with the morpheme index in the morpheme array as the source/target values
   */
  morphemeLinks = [];
  morphemes = [];
  for ( var i in rules) {
    /* make the @ more like what a linguist recognizes for word boundaries */
    if(rules[i].key.x == "@"){
      rules[i].key.x = "#_"
    }
    if(rules[i].key.y == "@"){
      rules[i].key.y = "_#"
    }
    var xpos = morphemes.indexOf(rules[i].key.x);
    if (xpos < 0) {
      morphemes.push(rules[i].key.x);
      xpos = morphemes.length - 1;
    }
    var ypos = morphemes.indexOf(rules[i].key.y);
    if (ypos < 0) {
      morphemes.push(rules[i].key.y);
      ypos = morphemes.length - 1;
    }
    //To avoid loops?
    if (rules[i].key.y.indexOf("@") == -1) {
      morphemeLinks.push({
        source : xpos,
        target : ypos,
        value : 1 //TODO use the context counting to get a weight measure
      });
    }
  }

  /*
   * Build the morphemes into nodes and color them by their morpheme length, could be a good measure of outliers
   */
  var morphemenodes = [];
  for (m in morphemes) {
    morphemenodes.push({
      name : morphemes[m],
      group : morphemes[m].length
    });
  }

  /*
   * Create the JSON required by D3
   */
  var rulesGraph = {};
  rulesGraph.links = morphemeLinks;
  rulesGraph.nodes = morphemenodes;
  Glosser.rulesGraph = rulesGraph;

  return rulesGraph;
}
Glosser.saveAndInterConnectInApp = function(callback){

  if(typeof callback == "function"){
    callback();
  }
}
/*
 * Some sample D3 from the force-html.html example
 *
 */
//Glosser.rulesGraph = Glosser.rulesGraph || {};
Glosser.visualizeMorphemesAsForceDirectedGraph = function(rulesGraph, divElement, pouchname){

  if(pouchname){
    Glosser.currentCorpusName = pouchname;
  }else{
    throw("Must provide corpus name to be able to visualize morphemes");
  }
  if(!rulesGraph){
    rulesGraph = Glosser.rulesGraph;
    if(rulesGraph){
      if(rulesGraph.links.length == 0){
        rulesGraph = Glosser.generateForceDirectedRulesJsonForD3();
      }
    }else{
      rulesGraph = Glosser.generateForceDirectedRulesJsonForD3();
    }
  }
  if(!rulesGraph){
    return;
  }
  if( Glosser.rulesGraph.links.length == 0 ){
    return;
  }
 json = rulesGraph;
  var width = 800,
  height = 300;

  var color = d3.scale.category20();

  var x = d3.scale.linear()
     .range([0, width]);

  var y = d3.scale.linear()
       .range([0, height - 40]);

  var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);

  var svg = d3.select("#corpus-precedence-rules-visualization-fullscreen").append("svg")
    .attr("width", width)
    .attr('title', "Morphology Visualization for "+ pouchname)
    .attr("height", height);

  var titletext = "Morphemes in your corpus";
  if(rulesGraph.nodes.length < 3){
    titletext = "Your morpheme visualizer will appear here after you have synced.";
  }
  //A label for the current year.
  var title = svg.append("text")
    .attr("class", "vis-title")
    .attr("dy", "1em")
    .attr("dx", "1em")
//    .attr("transform", "translate(" + x(1) + "," + y(1) + ")scale(-1,-1)")
    .text(titletext);

  var tooltip = null;

  //d3.json("./libs/rules.json", function(json) {
  force
      .nodes(json.nodes)
      .links(json.links)
      .start();

  var link = svg.selectAll("line.link")
      .data(json.links)
    .enter().append("line")
      .attr("class", "link")
      .style("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = svg.selectAll("circle.node")
      .data(json.nodes)
    .enter().append("circle")
      .attr("class", "node")
      .attr("r", 5)
      .style("fill", function(d) { return color(d.group); })
      .on("mouseover", function(d) {
        tooltip = d3.select("body")
        .append("div")
        .style("position", "absolute")
        .style("z-index", "10")
        .style("visibility", "visible")
        .style("color","#fff")
        .text(d.name)
      })
      .on("mouseout", function() {
        tooltip.style("visibility", "hidden");
      })
      .call(force.drag);

  node.append("title")
      .text(function(d) { return d.name; });

  force.on("tick", function() {
    link.attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

    node.attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
  });
  //});
}<commit_msg>fixes #954 can click on a morpheme bubble to search the corpus for that morpheme.<commit_after>var Glosser = Glosser || {};
Glosser.currentCorpusName = "";
Glosser.downloadPrecedenceRules = function(pouchname, glosserURL, callback){
  if(!glosserURL ||glosserURL == "default"){
    var couchConnection = app.get("corpus").get("couchConnection");
    var couchurl = OPrime.getCouchUrl(couchConnection);
    glosserURL = couchurl + "/_design/pages/_view/precedence_rules?group=true";
  }
  OPrime.makeCORSRequest({
    type : 'GET',
    url : glosserURL,
    success : function(rules) {
      localStorage.setItem(pouchname+"precendenceRules", JSON.stringify(rules.rows));

      // Reduce the rules such that rules which are found in multiple source
      // words are only used/included once.
      var reducedRules = _.chain(rules.rows).groupBy(function(rule) {
        return rule.key.x + "-" + rule.key.y;
      }).value();

      // Save the reduced precedence rules in localStorage
      localStorage.setItem(pouchname+"reducedRules", JSON.stringify(reducedRules));
      Glosser.currentCorpusName = pouchname;
      if(typeof callback == "function"){
        callback();
      }
    },
    error : function(e) {
      console.log("error getting precedence rules:", e);
    },
    dataType : ""
  });
};
/**
 * Takes in an utterance line and, based on our current set of precendence
 * rules, guesses what the morpheme line would be. The algorithm is
 * very conservative.
 *
 * @param {String} unparsedUtterance The raw utterance line.
 *
 * @return {String} The guessed morphemes line.
 */
Glosser.morphemefinder = function(unparsedUtterance) {
  var potentialParse = '';

  // Get the precedence rules from localStorage
  var rules = localStorage.getItem(Glosser.currentCorpusName+"reducedRules");

  var parsedWords = [];
  if (rules) {
    // Parse the rules from JSON into an object
    rules = JSON.parse(rules);

    // Divide the utterance line into words
    var unparsedWords = unparsedUtterance.trim().split(/ +/);

    for (var word in unparsedWords) {
      // Add the start/end-of-word character to the word
      unparsedWords[word] = "@" + unparsedWords[word] + "@";

      // Find the rules which match in local precedence
      var matchedRules = [];
      for (var r in rules) {
        if (unparsedWords[word].indexOf(r.replace(/-/, "")) >= 0) {
          matchedRules.push({
            r : rules[r]
          })
        }
      }

      // Attempt to find the longest template which the matching rules can
      // generate from start to end
      var prefixtemplate = [];
      prefixtemplate.push("@");
      for (var i = 0; i < 10; i++) {
        if (prefixtemplate[i] == undefined) {
          break;
        }
        for (var j in matchedRules) {
          if (prefixtemplate[i] == matchedRules[j].r[0].key.x) {
            if (prefixtemplate[i + 1]) { // ambiguity (two potential following
                                          // morphemes)
              prefixtemplate.pop();
              break;
            } else {
              prefixtemplate[i + 1] = matchedRules[j].r[0].key.y;
            }
          }
        }
      }

      // If the prefix template hit ambiguity in the middle, try from the suffix
      // in until it hits ambiguity
      var suffixtemplate = [];
      if (prefixtemplate[prefixtemplate.length - 1] != "@" || prefixtemplate.length == 1) {
        // Suffix:
        suffixtemplate.push("@")
        for (var i = 0; i < 10; i++) {
          if (suffixtemplate[i] == undefined) {
            break;
          }
          for (var j in matchedRules) {
            if (suffixtemplate[i] == matchedRules[j].r[0].key.y) {
              if (suffixtemplate[i + 1]) { // ambiguity (two potential
                                            // following morphemes)
                suffixtemplate.pop();
                break;
              } else {
                suffixtemplate[i + 1] = matchedRules[j].r[0].key.x;
              }
            }
          }
        }
      }

      // Combine prefix and suffix templates into one regular expression which
      // can be tested against the word to find a potential parse.
      // Regular expressions will look something like
      //    (@)(.*)(hall)(.*)(o)(.*)(wa)(.*)(n)(.*)(@)
      var template = [];
      template = prefixtemplate.concat(suffixtemplate.reverse())
      for (var slot in template) {
        template[slot] = "(" + template[slot] + ")";
      }
      var regex = new RegExp(template.join("(.*)"), "");

      // Use the regular expression to find a guessed morphemes line
      potentialParse = unparsedWords[word]
          .replace(regex, "$1-$2-$3-$4-$5-$6-$7-$8-$9") // Use backreferences to parse into morphemes
          .replace(/\$[0-9]/g, "")// Remove any backreferences that weren't used
          .replace(/@/g, "")      // Remove the start/end-of-line symbol
          .replace(/--+/g, "-")   // Ensure that there is only ever one "-" in a row
          .replace(/^-/, "")      // Remove "-" at the start of the word
          .replace(/-$/, "");     // Remove "-" at the end of the word
      if (OPrime.debugMode) OPrime.debug("Potential parse of " + unparsedWords[word].replace(/@/g, "")
          + " is " + potentialParse);

      parsedWords.push(potentialParse);
    }
  }

  return parsedWords.join(" ");
};
Glosser.toastedUserToSync = false;
Glosser.toastedUserToImport = 0;
Glosser.glossFinder = function(morphemesLine){
  //Guess a gloss
  var morphemeGroup = morphemesLine.split(/ +/);
  var glossGroups = [];
  if(! window.app.get("corpus")){
    return "";
  }
  if(! window.app.get("corpus").lexicon.get("lexiconNodes")){
    var corpusSize = 31; //TODO get corpus size another way. // app.get("corpus").datalists.models[app.get("corpus").datalists.models.length-1].get("datumIds").length;
    if(corpusSize > 30 && !Glosser.toastedUserToSync){
      Glosser.toastedUserToSync = true;
      window.appView.toastUser("You probably have enough data to train an autoglosser for your corpus.\n\nIf you sync your data with the team server then editing the morphemes will automatically run the auto glosser.","alert-success","Sync to train your auto-glosser:");
    }else{
      Glosser.toastedUserToImport ++;
      if(Glosser.toastedUserToImport % 10 == 1 && corpusSize < 30){
        window.appView.toastUser("You have roughly "+corpusSize+" datum saved in your pouch, if you have around 30 datum, then you have enough data to train an autoglosser for your corpus.","alert-info","AutoGlosser:");
      }
    }
    return "";
  }
  var lexiconNodes = window.app.get("corpus").lexicon.get("lexiconNodes");
  for (var group in morphemeGroup) {
    var morphemes = morphemeGroup[group].split("-");
    var glosses = [];
    for (var m in morphemes) {
      // Take the first gloss for this morpheme
      var matchingNode = _.max(lexiconNodes.where({morpheme: morphemes[m]}), function(node) { return node.get("value"); });
//      console.log(matchingNode);
      var gloss = "?";   // If there's no matching gloss, use question marks
      if (matchingNode) {
        gloss = matchingNode.get("gloss");
      }
      glosses.push(gloss);
    }

    glossGroups.push(glosses.join("-"));
  }

  // Replace the gloss line with the guessed glosses
  return glossGroups.join(" ");
};
/**
 * Takes as a parameters an array of rules which came from CouchDB precedence rule query.
 * Example Rule: {"key":{"x":"@","relation":"preceeds","y":"aqtu","context":"aqtu-nay-wa-n"},"value":2}
 */
Glosser.generateForceDirectedRulesJsonForD3 = function(rules, pouchname) {
  if(!pouchname){
    pouchname = Glosser.currentCorpusName;
  }
  if(!rules){
    rules = localStorage.getItem(pouchname+"precendenceRules");
    if(rules){
      rules = JSON.parse(rules);
    }
  }
  if(!rules ){
    return;
  }
  /*
   * Cycle through the precedence rules, convert them into graph edges with the morpheme index in the morpheme array as the source/target values
   */
  morphemeLinks = [];
  morphemes = [];
  for ( var i in rules) {
    /* make the @ more like what a linguist recognizes for word boundaries */
    if(rules[i].key.x == "@"){
      rules[i].key.x = "#_"
    }
    if(rules[i].key.y == "@"){
      rules[i].key.y = "_#"
    }
    var xpos = morphemes.indexOf(rules[i].key.x);
    if (xpos < 0) {
      morphemes.push(rules[i].key.x);
      xpos = morphemes.length - 1;
    }
    var ypos = morphemes.indexOf(rules[i].key.y);
    if (ypos < 0) {
      morphemes.push(rules[i].key.y);
      ypos = morphemes.length - 1;
    }
    //To avoid loops?
    if (rules[i].key.y.indexOf("@") == -1) {
      morphemeLinks.push({
        source : xpos,
        target : ypos,
        value : 1 //TODO use the context counting to get a weight measure
      });
    }
  }

  /*
   * Build the morphemes into nodes and color them by their morpheme length, could be a good measure of outliers
   */
  var morphemenodes = [];
  for (m in morphemes) {
    morphemenodes.push({
      name : morphemes[m],
      group : morphemes[m].length
    });
  }

  /*
   * Create the JSON required by D3
   */
  var rulesGraph = {};
  rulesGraph.links = morphemeLinks;
  rulesGraph.nodes = morphemenodes;
  Glosser.rulesGraph = rulesGraph;

  return rulesGraph;
}
Glosser.saveAndInterConnectInApp = function(callback){

  if(typeof callback == "function"){
    callback();
  }
}
/*
 * Some sample D3 from the force-html.html example
 *
 */
//Glosser.rulesGraph = Glosser.rulesGraph || {};
Glosser.visualizeMorphemesAsForceDirectedGraph = function(rulesGraph, divElement, pouchname){

  if(pouchname){
    Glosser.currentCorpusName = pouchname;
  }else{
    throw("Must provide corpus name to be able to visualize morphemes");
  }
  if(!rulesGraph){
    rulesGraph = Glosser.rulesGraph;
    if(rulesGraph){
      if(rulesGraph.links.length == 0){
        rulesGraph = Glosser.generateForceDirectedRulesJsonForD3();
      }
    }else{
      rulesGraph = Glosser.generateForceDirectedRulesJsonForD3();
    }
  }
  if(!rulesGraph){
    return;
  }
  if( Glosser.rulesGraph.links.length == 0 ){
    return;
  }
 json = rulesGraph;
  var width = 800,
  height = 300;

  var color = d3.scale.category20();

  var x = d3.scale.linear()
     .range([0, width]);

  var y = d3.scale.linear()
       .range([0, height - 40]);

  var force = d3.layout.force()
    .charge(-120)
    .linkStrength(0.2)
    .linkDistance(30)
    .size([width, height]);

  var svg = d3.select(divElement).append("svg")
    .attr("width", width)
    .attr('title', "Morphology Visualization for "+ pouchname)
    .attr("height", height);

  var titletext = "Click to search morphemes in your corpus";
  if(rulesGraph.nodes.length < 3){
    titletext = "Your morpheme visualizer will appear here after you have synced.";
  }
  //A label for the current year.
  var title = svg.append("text")
    .attr("class", "vis-title")
    .attr("dy", "1em")
    .attr("dx", "1em")
    .style("fill", "#cccccc")
//    .attr("transform", "translate(" + x(1) + "," + y(1) + ")scale(-1,-1)")
    .text(titletext);

  var tooltip = null;

  //d3.json("./libs/rules.json", function(json) {
  force
      .nodes(json.nodes)
      .links(json.links)
      .start();

  var link = svg.selectAll("line.link")
      .data(json.links)
    .enter().append("line")
      .attr("class", "link")
      .style("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = svg.selectAll("circle.node")
      .data(json.nodes)
    .enter().append("circle")
      .attr("class", "node")
      .attr("r", 5)
      .style("fill", function(d) { return color(d.group); })
      .on("mouseover", function(d) {
        tooltip = d3.select("body")
        .append("div")
        .style("position", "absolute")
        .style("z-index", "10")
        .style("visibility", "visible")
        .style("color","#fff")
        .text(d.name)
      })
      .on("mouseout", function() {
        tooltip.style("visibility", "hidden");
      })
      .on("click", function(d) {
        /* show the morpheme as a search result so the user can use the viz to explore the corpus*/
        if(window.app && window.app.router){
          // window.app.router.showEmbeddedSearch(pouchname, "morphemes:"+d.name);
          var url = "corpus/"+pouchname+"/search/"+"morphemes:"+d.name;
          // window.location.replace(url);
          window.app.router.navigate(url, {trigger: true});

        }
      })
      .call(force.drag);

  node.append("title")
      .text(function(d) { return d.name; });

  force.on("tick", function() {
    link.attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

    node.attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
  });
  //});
}<|endoftext|>


<commit_before>const wrapper = require("./ctx-level-helpers/wrapper")
const seq = require("reaks/seq")
const onEvent = require("reaks/onEvent")
const style = require("reaks/style")

module.exports = wrapper(action => {
  return seq([onEvent("click", action), style({ cursor: "pointer" })])
})
<commit_msg>stopPropagation in clickable<commit_after>const wrapper = require("./ctx-level-helpers/wrapper")
const seq = require("reaks/seq")
const onEvent = require("reaks/onEvent")
const style = require("reaks/style")

module.exports = wrapper(action => {
  return seq([
    onEvent("click", ev => {
      ev.stopPropagation()
      action(ev)
    }),
    style({ cursor: "pointer" }),
  ])
})
<|endoftext|>


<commit_before>const SAFE_ZONE_WIDTH = 640;
const SAFE_ZONE_HEIGHT = 960;

var game = new Phaser.Game(SAFE_ZONE_WIDTH, SAFE_ZONE_HEIGHT, Phaser.AUTO, '', {
    preload: preload,
    create: create,
    update: update
});

/*
    ============= Variables ==============
*/

var running = false;
var isPressed = false;

var player;
var coconut;
var bg;

var score = 0;
var scoreTimer;

var highscoresArray = [];
var highscoreText = "";

//localStorage.clear();

if (localStorage.getItem('highscores') == null) {
    var blankArray = [0];
    localStorage.setItem('highscores', JSON.stringify(blankArray));
} else {
    highscoresArray = JSON.parse(localStorage.getItem('highscores'));
}

/*
     ============= Preload ==============
*/
function preload() {

    // Loading all the game assets here
    game.load.image('bg', 'assets/img/bg.png');
    game.load.image('player', 'assets/img/player.png');
    game.load.image('coconut', 'assets/img/coconut.png');
    game.load.image('playagain', 'assets/img/playagain.png');
    game.load.image('startbutton', 'assets/img/start.png');
    game.load.image('highscorebutton', 'assets/img/highscore.png');
    game.load.image('highscorebg', 'assets/img/highscorebg.png');
}

function create() {
    // Some browsers have a useragent margin. Override this.
    $("body").css("margin", "0");

    /*
        ============= Scale ==============
    */

    // Aligning the game correctly for all devices
    game.scale.pageAlignVertically = true;
    game.scale.pageAlignHorizontally = true;
    game.stage.forcePortrait = true;
    window.checkScreenSize();
    window.resizeGame();

    game.input.maxPointers = 1;

    /*
        ============= Sprites ==============
    */

    // Adding a background image
    bg = game.add.image(0, 0, 'bg');
    bg.width = game.stage.bounds.width;
    bg.height = game.stage.bounds.height;

    /*
        ======= Game Score =======
    */

    // Creating the score text attributes
    var scoreText = score;
    var scoreStyle = {
        font: "15em Arial",
        fill: "#FFF",
        align: "center"
    };
    // Adding the scoreLabel to the game
    this.scoreLabel = game.add.text(game.world.centerX, game.world.centerY - 100, scoreText, scoreStyle);

    // Setting the score timer
    scoreTimer = game.time.create(false);
    scoreTimer.loop(1000, function () {
        score++
    }, this);

    // Adding the player sprite
    player = game.add.sprite(game.world.centerX, game.world.height - 100, 'player');
    // Setting it's anchor to the center
    player.anchor.set(0.5);

    // Adding the coconut sprite
    coconut = game.add.sprite(game.world.centerX, 50, 'coconut');
    // Setting it's anchor to the center
    coconut.anchor.setTo(0.5);

    /*
        ============= Input ==============
    */

    //Enabling input on player so that the user can drag to move
    player.inputEnabled = true;

    // Boolean of isPressed is set depending on the state of the onInputDown event
    player.events.onInputDown.add(function () {
        isPressed = true;
    }, this);
    player.events.onInputUp.add(function () {
        isPressed = false;
    }, this);

    /*

        ============= Physics ==============

    */

    // Enabling arcade physics
    game.physics.startSystem(Phaser.Physics.ARCADE);
    game.world.setBounds(0, 0, game.world.width, game.world.height);

    // Player stuff
    game.physics.enable(player, Phaser.Physics.ARCADE);
    player.enableBody = true;
    player.body.collideWorldBounds = true;
    player.body.width = 80;
    player.body.height = 80;
    player.body.offsetLeft = 10;
    player.body.offsetRight = 10;

    // Coconut stuff
    game.physics.enable(coconut, Phaser.Physics.ARCADE);
    coconut.enableBody = true;
    coconut.body.collideWorldBounds = true;
    coconut.body.bounce.setTo(1.3, 1.3);
    coconut.body.width = 80;
    coconut.body.height = 80;
    coconut.body.offsetLeft = 10;
    coconut.body.offsetRight = 10;
    coconut.body.drag = 0;

    // Initiate start menu
    startMenu();
}

/*
        ============= Start Menu ==============
*/

function startMenu() {

    // Create overlay and add to screen
    var startMenuOverlay = new Phaser.Graphics(this.game, 0, 0);
    startMenuOverlay.beginFill(0x000000, 1);
    startMenuOverlay.drawRect(0, 0, game.world.width, game.world.height);
    startMenuOverlay.endFill();

    this.startMenuOverlay = this.game.add.image(-10, -10, startMenuOverlay.generateTexture());

    // End of Overlay

    // Add the lose text
    var text = "Dodge Coconut";
    var style = {
        font: "7em Arial",
        fill: "#FFF",
        align: "center"
    };

    this.title = game.add.text(game.world.centerX - 250, game.world.centerY - 200, text, style);

    // Create Start Dodging button that goes over screen
    this.startButton = game.add.button(game.world.centerX - 125, game.world.centerY - 50, 'startbutton', function () {
        // Set running to true
        running = true;
        // Set all overlay sprites invisible
        this.startMenuOverlay.visible = false;
        this.startButton.visible = false;
        this.title.visible = false;
        this.highscoreButton.visible = false;
        this.highscores.visible = false;
        this.highscoresbg.visible = false;

    }, this);
    startButton.inputEnabled = true;

    this.highscoresbg = game.add.image(game.world.centerX - 128, game.world.centerY + 80, 'highscorebg');
    this.highscoresbg.visible = false;

    highscoresArray.sort(function (a, b) {
        return b - a
    });
    for (var current = 0; current < highscoresArray.length; current++) {
        highscoreText += highscoresArray[current] + "\n";
    }
    var style = {
        font: "4em Arial",
        fill: "#FFF",
        align: "center"
    };
    this.highscores = game.add.text(game.world.centerX - 20, game.world.centerY + 150, highscoreText, style);
    this.highscores.visible = false;

    // Create button that goes over screen
    this.highscoreButton = game.add.button(game.world.centerX - 60, game.world.centerY + 85, 'highscorebutton', function () {
        if (!this.highscores.visible) {
            this.highscores.visible = true;
            this.highscoresbg.visible = true;
        } else {
            this.highscores.visible = false;
            this.highscoresbg.visible = false;
        }
    }, this);
    this.highscoreButton.inputEnabled = true;

}

/*
        ============= Update ==============
*/
function update() {
    if (running) {
        playerListener();
        checkScreenSize();

        // Scorelabel proper updating
        this.scoreLabel.text = score;
        this.scoreLabel.x = game.world.centerX - this.scoreLabel.width / 2;

        // If the player and coconut collide, call hitCoconut
        game.physics.arcade.collide(player, coconut, hitCoconut, null, this);
        game.physics.arcade.overlap(player, coconut, hitCoconut, null, this);

    }
}

/*
        ============= Listeners ==============
*/

// Is called by update(), but is attentive to the boolean isPressed
function playerListener() {

    // Help text
    var text = "Dodge the coconut!";
    var style = {
        font: "5em Arial",
        fill: "#000",
        align: "center"
    };
    // Help text for game start
    if (this.helpText == null)
        this.helpText = game.add.text(player.x - 220, player.y - 120, text, style);

    // Actually checks if the player is pressed down
    if (isPressed) {
        if (helpText.visible) helpText.visible = false;

        var input_x = game.input.x;
        var input_y = game.input.y;

        if (player.x != input_x || player.y != input_y && player.checkWorldBounds) {
            player.x = input_x;
            player.y = input_y;
        }
        if (!scoreTimer.running) {
            scoreTimer.start();
        }
        game.physics.arcade.accelerateToObject(coconut, player, 600);
    }
}

/*
        ============= Collision ==============
*/
function hitCoconut(body1, body2) {
    running = false;

    recordHighscore();
    gameOver();

}

/*
        ============= Record High Score ==============
*/
function recordHighscore() {

    highscoresArray.sort(function (a, b) {
        return a - b
    });
    if (highscoresArray.length >= 3) {
        highscoresArray.push(score);
        highscoresArray = arrayTrim(highscoresArray, 3);
    } else {
        highscoresArray.push(score);
    }
    if (highscoresArray[0] == 0 || score > highscoresArray[0]) {
        highscoresArray[0] = score;
    }
    try {
        localStorage.setItem('highscores', JSON.stringify(highscoresArray));
    } catch (e) {
        alert("Error saving highscore.\n" + e.message);
    }
}
/*
        ============= Game Over Screen ==============
*/

function gameOver() {

    // Define then add the overlay
    var graphicOverlay = new Phaser.Graphics(this.game, 0, 0);
    graphicOverlay.beginFill(0x000000, 0.7);
    graphicOverlay.drawRect(0, 0, game.world.width, game.world.height);
    graphicOverlay.endFill();

    this.overlay = this.game.add.image(-10, -10, graphicOverlay.generateTexture());

    // Add the lose text
    var text = "Oh snap!";
    var style = {
        font: "10em Arial",
        fill: "#F00",
        align: "center"
    };

    var t = game.add.text(game.world.centerX - 200, game.world.centerY - 250, text, style);

    text = "Score: " + score + "s";
    style = {
        font: "5em Arial",
        fill: "#FFF",
        align: "center"
    };

    // Add the final score
    game.add.text(game.world.centerX - 100, game.world.centerY - 100, text, style);

    // Play again button
    var playAgainButton = game.add.button(game.world.centerX - 125, game.world.centerY, 'playagain', resetGame, this);
}

/*
        ============= Reset ==============
*/

function resetGame() {
    location.reload();
}

/*
        ============= Window Resize ==============
*/

function resizeGame() {

    game.scale.setExactFit();
    game.scale.refresh();

}

$(document).resize(function () {
    checkScreenSize();
    resizeGame();
});

function checkScreenSize() {
    var ww = window.innerWidth;
    var wh = window.innerHeight;

    if (ww / wh >= 4 / 3) {
        game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
    } else {
        game.scale.scaleMode = Phaser.ScaleManager.EXACT_FIT;
    }
}

/*
        ============= Misc Functions ==============
*/

function arrayTrim(array, trimTo) {
    array.sort(function (a, b) {
        return b - a;
    });
    var trimmedArray = [];
    for (var current = 0; current < trimTo; current++) {
        trimmedArray.push(array[current]);
    }
    return trimmedArray;
}<commit_msg>Added hover control for player<commit_after>const SAFE_ZONE_WIDTH = 640;
const SAFE_ZONE_HEIGHT = 960;

var game = new Phaser.Game(SAFE_ZONE_WIDTH, SAFE_ZONE_HEIGHT, Phaser.AUTO, '', {
    preload: preload,
    create: create,
    update: update
});

/*
    ============= Variables ==============
*/

var running = false;
var isPressed = false;

var player;
var coconut;
var bg;

var score = 0;
var scoreTimer;

var highscoresArray = [];
var highscoreText = "";

//localStorage.clear();

if (localStorage.getItem('highscores') == null) {
    var blankArray = [0];
    localStorage.setItem('highscores', JSON.stringify(blankArray));
} else {
    highscoresArray = JSON.parse(localStorage.getItem('highscores'));
}

/*
     ============= Preload ==============
*/
function preload() {

    // Loading all the game assets here
    game.load.image('bg', 'assets/img/bg.png');
    game.load.image('player', 'assets/img/player.png');
    game.load.image('coconut', 'assets/img/coconut.png');
    game.load.image('playagain', 'assets/img/playagain.png');
    game.load.image('startbutton', 'assets/img/start.png');
    game.load.image('highscorebutton', 'assets/img/highscore.png');
    game.load.image('highscorebg', 'assets/img/highscorebg.png');
}

function create() {
    // Some browsers have a useragent margin. Override this.
    $("body").css("margin", "0");

    /*
        ============= Scale ==============
    */

    // Aligning the game correctly for all devices
    game.scale.pageAlignVertically = true;
    game.scale.pageAlignHorizontally = true;
    game.stage.forcePortrait = true;
    window.checkScreenSize();
    window.resizeGame();

    game.input.maxPointers = 1;

    /*
        ============= Sprites ==============
    */

    // Adding a background image
    bg = game.add.image(0, 0, 'bg');
    bg.width = game.stage.bounds.width;
    bg.height = game.stage.bounds.height;

    /*
        ======= Game Score =======
    */

    // Creating the score text attributes
    var scoreText = score;
    var scoreStyle = {
        font: "15em Arial",
        fill: "#FFF",
        align: "center"
    };
    // Adding the scoreLabel to the game
    this.scoreLabel = game.add.text(game.world.centerX, game.world.centerY - 100, scoreText, scoreStyle);

    // Setting the score timer
    scoreTimer = game.time.create(false);
    scoreTimer.loop(1000, function () {
        score++
    }, this);

    // Adding the player sprite
    player = game.add.sprite(game.world.centerX, game.world.height - 100, 'player');
    // Setting it's anchor to the center
    player.anchor.set(0.5);

    // Adding the coconut sprite
    coconut = game.add.sprite(game.world.centerX, 50, 'coconut');
    // Setting it's anchor to the center
    coconut.anchor.setTo(0.5);

    /*
        ============= Input ==============
    */

    //Enabling input on player so that the user can drag to move
    player.inputEnabled = true;

    // Boolean of isPressed is set depending on the state of the onInputDown event
    player.events.onInputDown.add(function () {
        isPressed = true;
    }, this);
    player.events.onInputUp.add(function () {
        isPressed = false;
    }, this);
    player.events.onInputOver.add(function () {
        isPressed = true;
    }, this);

    /*

        ============= Physics ==============

    */

    // Enabling arcade physics
    game.physics.startSystem(Phaser.Physics.ARCADE);
    game.world.setBounds(0, 0, game.world.width, game.world.height);

    // Player stuff
    game.physics.enable(player, Phaser.Physics.ARCADE);
    player.enableBody = true;
    player.body.collideWorldBounds = true;
    player.body.width = 80;
    player.body.height = 80;
    player.body.offsetLeft = 10;
    player.body.offsetRight = 10;

    // Coconut stuff
    game.physics.enable(coconut, Phaser.Physics.ARCADE);
    coconut.enableBody = true;
    coconut.body.collideWorldBounds = true;
    coconut.body.bounce.setTo(1.3, 1.3);
    coconut.body.width = 80;
    coconut.body.height = 80;
    coconut.body.offsetLeft = 10;
    coconut.body.offsetRight = 10;
    coconut.body.drag = 0;

    // Initiate start menu
    startMenu();
}

/*
        ============= Start Menu ==============
*/

function startMenu() {

    // Create overlay and add to screen
    var startMenuOverlay = new Phaser.Graphics(this.game, 0, 0);
    startMenuOverlay.beginFill(0x000000, 1);
    startMenuOverlay.drawRect(0, 0, game.world.width, game.world.height);
    startMenuOverlay.endFill();

    this.startMenuOverlay = this.game.add.image(-10, -10, startMenuOverlay.generateTexture());

    // End of Overlay

    // Add the lose text
    var text = "Dodge Coconut";
    var style = {
        font: "7em Arial",
        fill: "#FFF",
        align: "center"
    };

    this.title = game.add.text(game.world.centerX - 250, game.world.centerY - 200, text, style);

    // Create Start Dodging button that goes over screen
    this.startButton = game.add.button(game.world.centerX - 125, game.world.centerY - 50, 'startbutton', function () {
        // Set running to true
        running = true;
        // Set all overlay sprites invisible
        this.startMenuOverlay.visible = false;
        this.startButton.visible = false;
        this.title.visible = false;
        this.highscoreButton.visible = false;
        this.highscores.visible = false;
        this.highscoresbg.visible = false;

    }, this);
    startButton.inputEnabled = true;

    this.highscoresbg = game.add.image(game.world.centerX - 128, game.world.centerY + 80, 'highscorebg');
    this.highscoresbg.visible = false;

    highscoresArray.sort(function (a, b) {
        return b - a
    });
    for (var current = 0; current < highscoresArray.length; current++) {
        highscoreText += highscoresArray[current] + "\n";
    }
    var style = {
        font: "4em Arial",
        fill: "#FFF",
        align: "center"
    };
    this.highscores = game.add.text(game.world.centerX - 20, game.world.centerY + 150, highscoreText, style);
    this.highscores.visible = false;

    // Create button that goes over screen
    this.highscoreButton = game.add.button(game.world.centerX - 60, game.world.centerY + 85, 'highscorebutton', function () {
        if (!this.highscores.visible) {
            this.highscores.visible = true;
            this.highscoresbg.visible = true;
        } else {
            this.highscores.visible = false;
            this.highscoresbg.visible = false;
        }
    }, this);
    this.highscoreButton.inputEnabled = true;

}

/*
        ============= Update ==============
*/
function update() {
    if (running) {
        playerListener();
        checkScreenSize();

        // Scorelabel proper updating
        this.scoreLabel.text = score;
        this.scoreLabel.x = game.world.centerX - this.scoreLabel.width / 2;

        // If the player and coconut collide, call hitCoconut
        game.physics.arcade.collide(player, coconut, hitCoconut, null, this);
        game.physics.arcade.overlap(player, coconut, hitCoconut, null, this);

    }
}

/*
        ============= Listeners ==============
*/

// Is called by update(), but is attentive to the boolean isPressed
function playerListener() {

    // Help text
    var text = "Dodge the coconut!";
    var style = {
        font: "5em Arial",
        fill: "#000",
        align: "center"
    };
    // Help text for game start
    if (this.helpText == null)
        this.helpText = game.add.text(player.x - 220, player.y - 120, text, style);

    // Actually checks if the player is pressed down
    if (isPressed) {
        if (helpText.visible) helpText.visible = false;

        var input_x = game.input.x;
        var input_y = game.input.y;

        if (player.x != input_x || player.y != input_y && player.checkWorldBounds) {
            player.x = input_x;
            player.y = input_y;
        }
        if (!scoreTimer.running) {
            scoreTimer.start();
        }
        game.physics.arcade.accelerateToObject(coconut, player, 600);
    }
}

/*
        ============= Collision ==============
*/
function hitCoconut(body1, body2) {
    running = false;

    recordHighscore();
    gameOver();

}

/*
        ============= Record High Score ==============
*/
function recordHighscore() {

    highscoresArray.sort(function (a, b) {
        return a - b
    });
    if (highscoresArray.length >= 3) {
        highscoresArray.push(score);
        highscoresArray = arrayTrim(highscoresArray, 3);
    } else {
        highscoresArray.push(score);
    }
    if (highscoresArray[0] == 0 || score > highscoresArray[0]) {
        highscoresArray[0] = score;
    }
    try {
        localStorage.setItem('highscores', JSON.stringify(highscoresArray));
    } catch (e) {
        alert("Error saving highscore.\n" + e.message);
    }
}
/*
        ============= Game Over Screen ==============
*/

function gameOver() {

    // Define then add the overlay
    var graphicOverlay = new Phaser.Graphics(this.game, 0, 0);
    graphicOverlay.beginFill(0x000000, 0.7);
    graphicOverlay.drawRect(0, 0, game.world.width, game.world.height);
    graphicOverlay.endFill();

    this.overlay = this.game.add.image(-10, -10, graphicOverlay.generateTexture());

    // Add the lose text
    var text = "Oh snap!";
    var style = {
        font: "10em Arial",
        fill: "#F00",
        align: "center"
    };

    var t = game.add.text(game.world.centerX - 200, game.world.centerY - 250, text, style);

    text = "Score: " + score + "s";
    style = {
        font: "5em Arial",
        fill: "#FFF",
        align: "center"
    };

    // Add the final score
    game.add.text(game.world.centerX - 100, game.world.centerY - 100, text, style);

    // Play again button
    var playAgainButton = game.add.button(game.world.centerX - 125, game.world.centerY, 'playagain', resetGame, this);
}

/*
        ============= Reset ==============
*/

function resetGame() {
    location.reload();
}

/*
        ============= Window Resize ==============
*/

function resizeGame() {

    game.scale.setExactFit();
    game.scale.refresh();

}

$(document).resize(function () {
    checkScreenSize();
    resizeGame();
});

function checkScreenSize() {
    var ww = window.innerWidth;
    var wh = window.innerHeight;

    if (ww / wh >= 4 / 3) {
        game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
    } else {
        game.scale.scaleMode = Phaser.ScaleManager.EXACT_FIT;
    }
}

/*
        ============= Misc Functions ==============
*/

function arrayTrim(array, trimTo) {
    array.sort(function (a, b) {
        return b - a;
    });
    var trimmedArray = [];
    for (var current = 0; current < trimTo; current++) {
        trimmedArray.push(array[current]);
    }
    return trimmedArray;
}<|endoftext|>