<commit_before>/**
 * Pascal.js
 * https://github.com/insanehong/Pascal.js
 *
 * Pascal.js is ans extendsive math library for javascript.
 * This library is designed to help people of web developer who are development of Statistics,Chart library, office application and analytics.
 *
 * Copyright (c) 2013 Insanehong and other contributors
 *
 * MIT LISENCE
 */
(function() {

  var root = this;

  var previousPascal = root.Pascal,
      undef = 'undefined';

  var Pascal  = function(obj) {
    if (obj instanceof Pascal) return obj;
    if (!(this instanceof Pascal)) return new Pascal(obj);
  };

  if(typeof exports !== 'undefined') {
    if(typeof module !== 'undefined' && module.exports) {
       exports = module.exports = Pascal;
    }
    exports.Pascal = Pascal;
  }

  if (typeof require !=='undefined' && typeof define !== 'undefined') {
    define(function() { return Pascal; });
  }

  if(typeof window !== 'undefined') {
    root.Pascal = Pascal;
  }

  Pascal.VERSION = '0.1.0';

  var matrix = Pascal.maxtrix = {},
      array = Pascal.array = {},
      PI = Pascal.PI = Math.PI,
      E = Pascal.E = Math.E,
      abs = Pascal.abs = Math.abs,
      pow = Pascal.pow = Math.pow,
      acos = Pascal.acos = Math.acos,
      atan2 = Pascal.atan2 = Math.atan2,
      cos = Pascal.cos = Math.cos,
      sin = Pascal.sin = Math.sin,
      tan = Pascal.tan = Math.tan,
      sqrt = Pascal.sqrt = Math.sqrt,
      exp = Pascal.exp = Math.exp;

  function ascSort(a,b) {
    return (a < b) ? -1 : (a > b) ? 1 : 0;
  }

  function descSort(a,b) {
    return (b < a) ? -1 : (b > a) ? 1 : 0;
  }

  function isNumber(n) {
    return (typeof n !== 'undefined' && n!==null && !isNaN(n)) ? true : false;
  }

  function isArray(array) {
    return (Object.prototype.toString.call(array) === '[object Array]') ? true : false;
  }

  function isEmpty(array) {
    return (typeof array === 'undefined' || array.length === 0 || array ===null) ? true : false;
  }

  var arrayMap = Pascal.arrayMap = function(array,fn) {
    var tmep =[],
        length = array.length,
        i;

    for(i=0; i < length; i++) tmep.push(fn.call(array,array[i],i)) ;
    return tmep;
  };

  var log = Pascal.log = function(x,b) {
    if(isNumber(x)) {
      x = Number(x);
      if(x <= 0) return NaN;
      return (typeof b === 'undefined') ? Math.log(x) : (b===1) ? 0 : Math.log(x) / Math.log(b);
    } else if(isArray(x)) {
      x = x.filter(isNumber);
      if(x.length === 0 ) return undef;
      return arrayMap(x,function(n) { return log(n,b); });
    }

    return undef;
  };

  var log10 = Pascal.log10 = function(x) {
    return log(x,10);
  };

  var log2 = Pascal.log2 = function(x) {
    return log(x,2);
  };

  var round = Pascal.round = function(x,digits) {
    if(isNumber(x)) {
      x = Number(x);
      return (digits === 'undefined' || isNaN(digits)) ? Math.round(x) : Math.round(x*pow(10,digits))/pow(10,digits);
    } else if(isArray(x)) {
      x = x.filter(isNumber);
      if(x.length === 0 ) return undef;
      return arrayMap(x,function(n) { return round(n,digits); });
    }

    return undef;
  };

  var ceil = Pascal.ceil = function(x,digits) {
    if(isNumber(x)) {
      x = Number(x);
      return (typeof digits === 'undefined' || isNaN(digits)) ? Math.ceil(x) : Math.ceil(x*pow(10,digits))/pow(10,digits);
    } else if(isArray(x)) {
      x = x.filter(isNumber);
      if(x.length === 0 ) return undef;
      return arrayMap(x,function(n) { return ceil(n,digits); });
    }

    return undef;
  };

  var floor = Pascal.floor = function(x,digits) {
    if(isNumber(x)) {
      x = Number(x);
      return (typeof digits === 'undefined' || isNaN(digits)) ? Math.floor(x) : Math.floor(x*pow(10,digits))/pow(10,digits);
    } else if(isArray(x)) {
      x = x.filter(isNumber);
      if(x.length === 0 ) return undef;
      return arrayMap(x,function(n) { return floor(n,digits); });
    }
    return undef;
  };

   var perm = Pascal.perm = function(n,r) {
    if(isNaN(n) || isNaN(r)) return NaN;
    if(n <= r) return fact(n);
    return (r===0) ? 1 : parseInt(n * perm(n-1,r-1),10);
  };

  var gamma = Pascal.gamma = function(z) {
    if(isNumber(z)) {
      z = Number(z);
      var g = 7,
          p = [0.99999999999980993, 676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059, 12.507343278686905,
              -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7],
          x = p[0],
          length = p.length,
          i, t, rs;

      if(z < 0.5 ) return PI / ( sin(PI * z) * gamma(1 - z));

      z -= 1;
      t = z + g + 0.5;

      for (i=1 ; i < length; i++) x += p[i] / (z + i );

      rs = parseFloat(sqrt(2 * PI) * pow(t, (z + 0.5)) * exp(-t) * x);
      return (z === floor(z)) ? round(rs) : rs;
    } else if(isArray(z)) {
       z = z.filter(isNumber);
      if(z.length === 0 ) return undef;
      return arrayMap(z,function(n) { return gamma(n); });
    }
    return undef;
  };

  var beta = Pascal.beta = function(a,b) {
    return (a < 0 || b < 0) ? NaN : (gamma(a) * gamma(b)) / gamma(a+b);
  };

  var fact = Pascal.fact = function(num) {
    if(isNaN(num)) return NaN;
    return (num < 0) ? -1 : (num === 0) ? 1 : gamma(num+1);
  };

  var random = Pascal.random = function(min,max) {
    return floor(Math.random() * (max - min + 1)) + min;
  };

  var add = Pascal.add = function(data,an) {
    data = data.filter(isNumber);
    if(data.length===0) return null;
    data = arrayMap(data,function(n) { return n + an; });
    return data ;
  };

  var multiply = Pascal.multiply = function(data,number) {
    data = data.filter(isNumber);
    if(data.length===0) return null;
    data = arrayMap(data,function(n) { return n * number; });
    return data ;
  };

  var sum = Pascal.sum =  function(data,fn) {
    var total = 0,
        leng = 0,
        result,
        i;

    if(typeof fn === 'undefined') {
      data = data.filter(isNumber);
      leng = data.length;
      for(i=0; i < leng; i++) {
        total+= Number(data[i]);
      }
    } else {
      leng = data.length;
      for(i=0; i < leng; i++) total+=(isNaN(result = fn.call(data,data[i],i))) ? 0 : Number(result);
    }

    return total;
  };

  var mean = Pascal.mean = function(data,fn) {
    var total=0,
        i,
        result;

    if(typeof fn !== 'undefined') data = arrayMap(data,fn);

    data = data.filter(isNumber);
    total = sum(data);
    return total / data.length;
  };

  var variance = Pascal.variance = function(data,fn) {
    var vmean,sumstd;

    if(typeof fn !== 'undefined') data = arrayMap(data,fn);

    vmean = mean(data);
    sumstd = sum(data,function(n) { return pow(n-vmean,2); });
    return sumstd / (data.length -1);
  };

  var varp = Pascal.varp = function(data,fn) {
    var powmean,meanpow;

    if(typeof fn !== 'undefined') data = arrayMap(data,fn);
    console.log(data);
    console.log(mean(data));
    meanpow = pow(mean(data),2);

    powmean = mean(data,function(n){ return pow(n,2); });
    console.log(meanpow);
    console.log(powmean);
    console.log(powmean - meanpow);
    return powmean - meanpow;
  };

  var stdev = Pascal.stdev = function(data,fn) {
    return sqrt(variance(data,fn));
  };

  var stdevp = Pascal.stdevp = function(data,fn) {
    return sqrt(varp(data,fn));
  };

  var median = Pascal.median = function(data,fn) {
    return quantile(data,0.5,fn);
  };

  var quantile = Pascal.quantile = function(data,p,fn) {
    var h , fh, N , i, q;

    if(typeof fn !== 'undefined') data = arrayMap(data,fn);

    data = data.filter(isNumber).sort(ascSort);

    N = data.length;

    if(N === 0 ) return null;

    h = ((N - 1) * p) +1;
    hf = floor(h);
    i = hf -1;

    q = data[i] + ((h-hf)*(data[hf] - data[i]));

    return q;
  };

  var quartile = Pascal.quartile = function(data,fn) {
    var q1, q2, q3, q4;
    if(typeof fn !== 'undefined') data = arrayMap(data,fn);

    data = data.filter(isNumber).sort(ascSort);

    q0 = min(data);
    q1 = quantile(data,0.25),
    q2 = quantile(data,0.5),
    q3 = quantile(data,0.75),
    q4 = max(data);

    return (data.length === 0 ) ? null : [q0,q1,q2,q3,q4];
  };

  var min = Pascal.min = function(data,fn) {
    if(typeof fn !== 'undefined') data = arrayMap(data,fn);
    data = data.filter(isNumber);
    return (isEmpty(data)) ? null :  Math.min.apply(null,data);
  };

  var max = Pascal.max = function(data,fn) {
    if(typeof fn !== 'undefined') data = arrayMap(data,fn);
    data = data.filter(isNumber);
    return (isEmpty(data)) ? null :  Math.max.apply(null,data);
  };

  var minmax = Pascal.minmax = function(data,fn) {
    if(typeof fn !== 'undefined') data = arrayMap(data,fn);
    data = data.filter(isNumber);
    return (data.length ===0) ? null : (data.length === 1 ) ? [data,data] : [min(data), max(data)];
  };

  var cov = Pascal.cov = function(arrayX,arrayY) {
    var X = arrayX.filter(isNumber),
        Y = arrayY.filter(isNumber),
        meanX = mean(X),
        meanY = mean(Y),
        covData = [],
        i = 0, total=0;

    if(X.length !== Y.length || X.length===0) return NaN;

    for( ; i < X.length ; i++ ) {
      covData.push( (X[i] - meanX) * (Y[i] - meanY) );
    }

    total = sum(covData);
    return total / (X.length -1) ;
  };

  var covp = Pascal.covp = function(arrayX,arrayY) {
    var X = arrayX.filter(isNumber),
        Y = arrayY.filter(isNumber),
        XY = [],
        temp = 0;

    if(X.length !== Y.length || X.length===0) return NaN;

    for(var i = 0; i < X.length ; i++ ) {
      XY.push(X[i] * Y[i]);
    }

    var meanX = mean(X),
        meanY = mean(Y),
        meanXY = mean(XY);

    return meanXY -  (meanX * meanY) ;
  };

}).call(this);<commit_msg>remove console code for ouput test<commit_after>/**
 * Pascal.js
 * https://github.com/insanehong/Pascal.js
 *
 * Pascal.js is ans extendsive math library for javascript.
 * This library is designed to help people of web developer who are development of Statistics,Chart library, office application and analytics.
 *
 * Copyright (c) 2013 Insanehong and other contributors
 *
 * MIT LISENCE
 */
(function() {

  var root = this;

  var previousPascal = root.Pascal,
      undef = 'undefined';

  var Pascal  = function(obj) {
    if (obj instanceof Pascal) return obj;
    if (!(this instanceof Pascal)) return new Pascal(obj);
  };

  if(typeof exports !== 'undefined') {
    if(typeof module !== 'undefined' && module.exports) {
       exports = module.exports = Pascal;
    }
    exports.Pascal = Pascal;
  }

  if (typeof require !=='undefined' && typeof define !== 'undefined') {
    define(function() { return Pascal; });
  }

  if(typeof window !== 'undefined') {
    root.Pascal = Pascal;
  }

  Pascal.VERSION = '0.1.0';

  var matrix = Pascal.maxtrix = {},
      array = Pascal.array = {},
      PI = Pascal.PI = Math.PI,
      E = Pascal.E = Math.E,
      abs = Pascal.abs = Math.abs,
      pow = Pascal.pow = Math.pow,
      acos = Pascal.acos = Math.acos,
      atan2 = Pascal.atan2 = Math.atan2,
      cos = Pascal.cos = Math.cos,
      sin = Pascal.sin = Math.sin,
      tan = Pascal.tan = Math.tan,
      sqrt = Pascal.sqrt = Math.sqrt,
      exp = Pascal.exp = Math.exp;

  function ascSort(a,b) {
    return (a < b) ? -1 : (a > b) ? 1 : 0;
  }

  function descSort(a,b) {
    return (b < a) ? -1 : (b > a) ? 1 : 0;
  }

  function isNumber(n) {
    return (typeof n !== 'undefined' && n!==null && !isNaN(n)) ? true : false;
  }

  function isArray(array) {
    return (Object.prototype.toString.call(array) === '[object Array]') ? true : false;
  }

  function isEmpty(array) {
    return (typeof array === 'undefined' || array.length === 0 || array ===null) ? true : false;
  }

  var arrayMap = Pascal.arrayMap = function(array,fn) {
    var tmep =[],
        length = array.length,
        i;

    for(i=0; i < length; i++) tmep.push(fn.call(array,array[i],i)) ;
    return tmep;
  };

  var log = Pascal.log = function(x,b) {
    if(isNumber(x)) {
      x = Number(x);
      if(x <= 0) return NaN;
      return (typeof b === 'undefined') ? Math.log(x) : (b===1) ? 0 : Math.log(x) / Math.log(b);
    } else if(isArray(x)) {
      x = x.filter(isNumber);
      if(x.length === 0 ) return undef;
      return arrayMap(x,function(n) { return log(n,b); });
    }

    return undef;
  };

  var log10 = Pascal.log10 = function(x) {
    return log(x,10);
  };

  var log2 = Pascal.log2 = function(x) {
    return log(x,2);
  };

  var round = Pascal.round = function(x,digits) {
    if(isNumber(x)) {
      x = Number(x);
      return (digits === 'undefined' || isNaN(digits)) ? Math.round(x) : Math.round(x*pow(10,digits))/pow(10,digits);
    } else if(isArray(x)) {
      x = x.filter(isNumber);
      if(x.length === 0 ) return undef;
      return arrayMap(x,function(n) { return round(n,digits); });
    }

    return undef;
  };

  var ceil = Pascal.ceil = function(x,digits) {
    if(isNumber(x)) {
      x = Number(x);
      return (typeof digits === 'undefined' || isNaN(digits)) ? Math.ceil(x) : Math.ceil(x*pow(10,digits))/pow(10,digits);
    } else if(isArray(x)) {
      x = x.filter(isNumber);
      if(x.length === 0 ) return undef;
      return arrayMap(x,function(n) { return ceil(n,digits); });
    }

    return undef;
  };

  var floor = Pascal.floor = function(x,digits) {
    if(isNumber(x)) {
      x = Number(x);
      return (typeof digits === 'undefined' || isNaN(digits)) ? Math.floor(x) : Math.floor(x*pow(10,digits))/pow(10,digits);
    } else if(isArray(x)) {
      x = x.filter(isNumber);
      if(x.length === 0 ) return undef;
      return arrayMap(x,function(n) { return floor(n,digits); });
    }
    return undef;
  };

   var perm = Pascal.perm = function(n,r) {
    if(isNaN(n) || isNaN(r)) return NaN;
    if(n <= r) return fact(n);
    return (r===0) ? 1 : parseInt(n * perm(n-1,r-1),10);
  };

  var gamma = Pascal.gamma = function(z) {
    if(isNumber(z)) {
      z = Number(z);
      var g = 7,
          p = [0.99999999999980993, 676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059, 12.507343278686905,
              -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7],
          x = p[0],
          length = p.length,
          i, t, rs;

      if(z < 0.5 ) return PI / ( sin(PI * z) * gamma(1 - z));

      z -= 1;
      t = z + g + 0.5;

      for (i=1 ; i < length; i++) x += p[i] / (z + i );

      rs = parseFloat(sqrt(2 * PI) * pow(t, (z + 0.5)) * exp(-t) * x);
      return (z === floor(z)) ? round(rs) : rs;
    } else if(isArray(z)) {
       z = z.filter(isNumber);
      if(z.length === 0 ) return undef;
      return arrayMap(z,function(n) { return gamma(n); });
    }
    return undef;
  };

  var beta = Pascal.beta = function(a,b) {
    return (a < 0 || b < 0) ? NaN : (gamma(a) * gamma(b)) / gamma(a+b);
  };

  var fact = Pascal.fact = function(num) {
    if(isNaN(num)) return NaN;
    return (num < 0) ? -1 : (num === 0) ? 1 : gamma(num+1);
  };

  var random = Pascal.random = function(min,max) {
    return floor(Math.random() * (max - min + 1)) + min;
  };

  var add = Pascal.add = function(data,an) {
    data = data.filter(isNumber);
    if(data.length===0) return null;
    data = arrayMap(data,function(n) { return n + an; });
    return data ;
  };

  var multiply = Pascal.multiply = function(data,number) {
    data = data.filter(isNumber);
    if(data.length===0) return null;
    data = arrayMap(data,function(n) { return n * number; });
    return data ;
  };

  var sum = Pascal.sum =  function(data,fn) {
    var total = 0,
        leng = 0,
        result,
        i;

    if(typeof fn === 'undefined') {
      data = data.filter(isNumber);
      leng = data.length;
      for(i=0; i < leng; i++) {
        total+= Number(data[i]);
      }
    } else {
      leng = data.length;
      for(i=0; i < leng; i++) total+=(isNaN(result = fn.call(data,data[i],i))) ? 0 : Number(result);
    }

    return total;
  };

  var mean = Pascal.mean = function(data,fn) {
    var total=0,
        i,
        result;

    if(typeof fn !== 'undefined') data = arrayMap(data,fn);

    data = data.filter(isNumber);
    total = sum(data);
    return total / data.length;
  };

  var variance = Pascal.variance = function(data,fn) {
    var vmean,sumstd;

    if(typeof fn !== 'undefined') data = arrayMap(data,fn);

    vmean = mean(data);
    sumstd = sum(data,function(n) { return pow(n-vmean,2); });
    return sumstd / (data.length -1);
  };

  var varp = Pascal.varp = function(data,fn) {
    var powmean,meanpow;

    if(typeof fn !== 'undefined') data = arrayMap(data,fn);
    meanpow = pow(mean(data),2);
    powmean = mean(data,function(n){ return pow(n,2); });

    return powmean - meanpow;
  };

  var stdev = Pascal.stdev = function(data,fn) {
    return sqrt(variance(data,fn));
  };

  var stdevp = Pascal.stdevp = function(data,fn) {
    return sqrt(varp(data,fn));
  };

  var median = Pascal.median = function(data,fn) {
    return quantile(data,0.5,fn);
  };

  var quantile = Pascal.quantile = function(data,p,fn) {
    var h , fh, N , i, q;

    if(typeof fn !== 'undefined') data = arrayMap(data,fn);

    data = data.filter(isNumber).sort(ascSort);

    N = data.length;

    if(N === 0 ) return null;

    h = ((N - 1) * p) +1;
    hf = floor(h);
    i = hf -1;

    q = data[i] + ((h-hf)*(data[hf] - data[i]));

    return q;
  };

  var quartile = Pascal.quartile = function(data,fn) {
    var q1, q2, q3, q4;
    if(typeof fn !== 'undefined') data = arrayMap(data,fn);

    data = data.filter(isNumber).sort(ascSort);

    q0 = min(data);
    q1 = quantile(data,0.25),
    q2 = quantile(data,0.5),
    q3 = quantile(data,0.75),
    q4 = max(data);

    return (data.length === 0 ) ? null : [q0,q1,q2,q3,q4];
  };

  var min = Pascal.min = function(data,fn) {
    if(typeof fn !== 'undefined') data = arrayMap(data,fn);
    data = data.filter(isNumber);
    return (isEmpty(data)) ? null :  Math.min.apply(null,data);
  };

  var max = Pascal.max = function(data,fn) {
    if(typeof fn !== 'undefined') data = arrayMap(data,fn);
    data = data.filter(isNumber);
    return (isEmpty(data)) ? null :  Math.max.apply(null,data);
  };

  var minmax = Pascal.minmax = function(data,fn) {
    if(typeof fn !== 'undefined') data = arrayMap(data,fn);
    data = data.filter(isNumber);
    return (data.length ===0) ? null : (data.length === 1 ) ? [data,data] : [min(data), max(data)];
  };

  var cov = Pascal.cov = function(arrayX,arrayY) {
    var X = arrayX.filter(isNumber),
        Y = arrayY.filter(isNumber),
        meanX = mean(X),
        meanY = mean(Y),
        covData = [],
        i = 0, total=0;

    if(X.length !== Y.length || X.length===0) return NaN;

    for( ; i < X.length ; i++ ) {
      covData.push( (X[i] - meanX) * (Y[i] - meanY) );
    }

    total = sum(covData);
    return total / (X.length -1) ;
  };

  var covp = Pascal.covp = function(arrayX,arrayY) {
    var X = arrayX.filter(isNumber),
        Y = arrayY.filter(isNumber),
        XY = [],
        temp = 0;

    if(X.length !== Y.length || X.length===0) return NaN;

    for(var i = 0; i < X.length ; i++ ) {
      XY.push(X[i] * Y[i]);
    }

    var meanX = mean(X),
        meanY = mean(Y),
        meanXY = mean(XY);

    return meanXY -  (meanX * meanY) ;
  };

}).call(this);<|endoftext|>


<commit_before>import React from 'react';
import CSSModules from 'react-css-modules';
import elasticlunr from 'elasticlunr';
import 'semantic-ui-css/semantic.min.css';
import request from 'superagent';

import '../css/base.css';
import css from './home.css';

import ResultsLoader from './ResultsLoader';
import CourseProData from './models/DataLib';
import Keys from './models/Keys';

const searchConfig = {
  fields: {
    classId: {
      boost: 4,
    },
    subject: {
      boost: 2,
    },
    desc: {
      boost: 1,
    },
    name: {
      boost: 1,
    },
    profs: {
      boost: 1,
    },
    locations: {
      boost: 1,
    },
    crns: {
      boost: 1,
    },
  },
  expand: true,
};

// Home page component
class Home extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      results: [],
    };

    this.dataPromise = null;

    this.searchIndex = null;
    this.termData = null;
    this.employeeMap = null;
    this.employeesSearchIndex = null;

    this.onClick = this.onClick.bind(this);

    this.loadData();
  }

  async loadData() {
    const promises = [];

    promises.push(request('data/getSearchIndex/neu.edu/201810').then((res) => {
      this.searchIndex = elasticlunr.Index.load(JSON.parse(res.text));
    }));

    promises.push(request('data/getTermDump/neu.edu/201810').then((res) => {
      this.termData = CourseProData.loadData(JSON.parse(res.text));
    }));

    promises.push(request('data/employeeMap.json').then((res) => {
      this.employeeMap = JSON.parse(res.text);
    }));

    promises.push(request('data/employeesSearchIndex.json').then((res) => {
      this.employeesSearchIndex = elasticlunr.Index.load(JSON.parse(res.text));
    }));

    this.dataPromise = Promise.all(promises).then(() => {
      // TODO remove
      // test go through classes and make sure they are all in sections?
      // 3 invalid crns (or missing sections?) were found with this code
      Object.values(this.termData.termDump.classMap).forEach((aClass) => {
        if (!aClass.crns) {
          return;
        }

        aClass.crns.forEach((crn) => {
          const keys = Keys.create({
            host: aClass.host,
            termId: aClass.termId,
            subject: aClass.subject,
            classUid: aClass.classUid,
            crn: crn,
          });

          if (!keys) {
            console.error('lol', aClass, crn);
          }

          const sectionServerData = this.termData.termDump.sectionMap[keys.getHash()];
          if (!sectionServerData) {
            console.error('wtf', aClass, crn);
          }
        });
      });
    });
  }

  // TODO This is just for testing
  async componentDidMount() {
    await this.dataPromise;
    this.search('huntington');
  }

  async search(searchTerm) {
    // Ensure that the data has loaded
    await this.dataPromise;

    // This is O(n), but because there are so few subjects it usually takes < 1ms
    // If the search term starts with a subject (eg cs2500), put a space after the subject
    const lowerCaseSearchTerm = searchTerm.toLowerCase();
    const subjects = this.termData.getSubjects();

    for (let i = 0; i < subjects.length; i++) {
      const subject = subjects[i];
      if (lowerCaseSearchTerm.startsWith(subject.subject.toLowerCase())) {
        const remainingSearch = searchTerm.slice(subject.subject.length);

        // Only rewrite the search if the rest of the query has a high probability of being a classId.
        if (remainingSearch.length > 5) {
          break;
        }
        const match = remainingSearch.match(/\d/g);

        if (!match || match.length < 3) {
          break;
        } else {
          searchTerm = `${searchTerm.slice(0, subject.subject.length)} ${searchTerm.slice(subject.subject.length)}`;
        }
        break;
      }
    }

    // Returns an array of objects that has a .ref and a .score
    // The array is sorted by score (with the highest matching closest to the beginning)
    // eg {ref:"neu.edu/201710/ARTF/1123_1835962771", score: 3.1094880801464573}
    const classResults = this.searchIndex.search(searchTerm, searchConfig);

    const employeeResults = this.employeesSearchIndex.search(searchTerm, {});

    const output = [];

    // This takes no time at all, never more than 2ms and usally <1ms
    while (true) {
      if (classResults.length === 0 && employeeResults.length === 0) {
        break;
      }

      if (classResults.length === 0) {
        output.push({
          ref: employeeResults[0].ref,
          type: 'employee',
        });
        employeeResults.splice(0, 1);
        continue;
      }

      if (employeeResults.length === 0) {
        output.push({
          type: 'class',
          ref: classResults[0].ref,
        });

        classResults.splice(0, 1);
        continue;
      }

      if (classResults[0].score > employeeResults[0].score) {
        output.push({
          type: 'class',
          ref: classResults[0].ref,
        });
        classResults.splice(0, 1);
        continue;
      }

      if (classResults[0].score <= employeeResults[0].score) {
        output.push({
          ref: employeeResults[0].ref,
          type: 'employee',
        });
        employeeResults.splice(0, 1);
      }
    }

    this.setState({
      results: output,
    });
  }

  onClick(event) {
    if (!event.target.value) {
      this.setState({
        searchResults: [],
      });
      return;
    }

    this.search(event.target.value);
  }

  render() {

    if (!this.employeeMap || !this.termData) {
      return null;
    }

    return (

          <svg width="80" height="80" viewBox="0 0 250 250" className = { css.githubCornerIcon } >
            <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
            <path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" className = {css.octopusArm + " octo-arm"}></path>
            <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" className = "octo-body"></path>
          </svg>

            Search

           For Northeastern

              <input
                autoFocus type='search'
                id='seach_id'
                placeholder='Search Classes, Professors, and Employees'
                autoComplete='off'
                spellCheck='false'
                tabIndex='0'
                onChange={ this.onClick }
              />

          <ResultsLoader
            results={ this.state.results }
            termData={ this.termData }
            employeeMap={ this.employeeMap }
          />

          See an issue or want to add to this website? Fork it or create an issue on
          GitHub.

          Made with

          by
          Ryan Hughes
          and UI borrowed from
          Two Factor Authenticaton.

    );
  }
}

export default CSSModules(Home, css);
<commit_msg>fixed spacing in the footer of the page.<commit_after>import React from 'react';
import CSSModules from 'react-css-modules';
import elasticlunr from 'elasticlunr';
import 'semantic-ui-css/semantic.min.css';
import request from 'superagent';

import '../css/base.css';
import css from './home.css';

import ResultsLoader from './ResultsLoader';
import CourseProData from './models/DataLib';
import Keys from './models/Keys';

const searchConfig = {
  fields: {
    classId: {
      boost: 4,
    },
    subject: {
      boost: 2,
    },
    desc: {
      boost: 1,
    },
    name: {
      boost: 1,
    },
    profs: {
      boost: 1,
    },
    locations: {
      boost: 1,
    },
    crns: {
      boost: 1,
    },
  },
  expand: true,
};

// Home page component
class Home extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      results: [],
    };

    this.dataPromise = null;

    this.searchIndex = null;
    this.termData = null;
    this.employeeMap = null;
    this.employeesSearchIndex = null;

    this.onClick = this.onClick.bind(this);

    this.loadData();
  }

  async loadData() {
    const promises = [];

    promises.push(request('data/getSearchIndex/neu.edu/201810').then((res) => {
      this.searchIndex = elasticlunr.Index.load(JSON.parse(res.text));
    }));

    promises.push(request('data/getTermDump/neu.edu/201810').then((res) => {
      this.termData = CourseProData.loadData(JSON.parse(res.text));
    }));

    promises.push(request('data/employeeMap.json').then((res) => {
      this.employeeMap = JSON.parse(res.text);
    }));

    promises.push(request('data/employeesSearchIndex.json').then((res) => {
      this.employeesSearchIndex = elasticlunr.Index.load(JSON.parse(res.text));
    }));

    this.dataPromise = Promise.all(promises).then(() => {
      // TODO remove
      // test go through classes and make sure they are all in sections?
      // 3 invalid crns (or missing sections?) were found with this code
      Object.values(this.termData.termDump.classMap).forEach((aClass) => {
        if (!aClass.crns) {
          return;
        }

        aClass.crns.forEach((crn) => {
          const keys = Keys.create({
            host: aClass.host,
            termId: aClass.termId,
            subject: aClass.subject,
            classUid: aClass.classUid,
            crn: crn,
          });

          if (!keys) {
            console.error('lol', aClass, crn);
          }

          const sectionServerData = this.termData.termDump.sectionMap[keys.getHash()];
          if (!sectionServerData) {
            console.error('wtf', aClass, crn);
          }
        });
      });
    });
  }

  // TODO This is just for testing
  async componentDidMount() {
    await this.dataPromise;
    this.search('huntington');
  }

  async search(searchTerm) {
    // Ensure that the data has loaded
    await this.dataPromise;

    // This is O(n), but because there are so few subjects it usually takes < 1ms
    // If the search term starts with a subject (eg cs2500), put a space after the subject
    const lowerCaseSearchTerm = searchTerm.toLowerCase();
    const subjects = this.termData.getSubjects();

    for (let i = 0; i < subjects.length; i++) {
      const subject = subjects[i];
      if (lowerCaseSearchTerm.startsWith(subject.subject.toLowerCase())) {
        const remainingSearch = searchTerm.slice(subject.subject.length);

        // Only rewrite the search if the rest of the query has a high probability of being a classId.
        if (remainingSearch.length > 5) {
          break;
        }
        const match = remainingSearch.match(/\d/g);

        if (!match || match.length < 3) {
          break;
        } else {
          searchTerm = `${searchTerm.slice(0, subject.subject.length)} ${searchTerm.slice(subject.subject.length)}`;
        }
        break;
      }
    }

    // Returns an array of objects that has a .ref and a .score
    // The array is sorted by score (with the highest matching closest to the beginning)
    // eg {ref:"neu.edu/201710/ARTF/1123_1835962771", score: 3.1094880801464573}
    const classResults = this.searchIndex.search(searchTerm, searchConfig);

    const employeeResults = this.employeesSearchIndex.search(searchTerm, {});

    const output = [];

    // This takes no time at all, never more than 2ms and usally <1ms
    while (true) {
      if (classResults.length === 0 && employeeResults.length === 0) {
        break;
      }

      if (classResults.length === 0) {
        output.push({
          ref: employeeResults[0].ref,
          type: 'employee',
        });
        employeeResults.splice(0, 1);
        continue;
      }

      if (employeeResults.length === 0) {
        output.push({
          type: 'class',
          ref: classResults[0].ref,
        });

        classResults.splice(0, 1);
        continue;
      }

      if (classResults[0].score > employeeResults[0].score) {
        output.push({
          type: 'class',
          ref: classResults[0].ref,
        });
        classResults.splice(0, 1);
        continue;
      }

      if (classResults[0].score <= employeeResults[0].score) {
        output.push({
          ref: employeeResults[0].ref,
          type: 'employee',
        });
        employeeResults.splice(0, 1);
      }
    }

    this.setState({
      results: output,
    });
  }

  onClick(event) {
    if (!event.target.value) {
      this.setState({
        searchResults: [],
      });
      return;
    }

    this.search(event.target.value);
  }

  render() {

    if (!this.employeeMap || !this.termData) {
      return null;
    }

    return (

          <svg width="80" height="80" viewBox="0 0 250 250" className = { css.githubCornerIcon } >
            <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
            <path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" className = {css.octopusArm + " octo-arm"}></path>
            <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" className = "octo-body"></path>
          </svg>

            Search

           For Northeastern

              <input
                autoFocus type='search'
                id='seach_id'
                placeholder='Search Classes, Professors, and Employees'
                autoComplete='off'
                spellCheck='false'
                tabIndex='0'
                onChange={ this.onClick }
              />

          <ResultsLoader
            results={ this.state.results }
            termData={ this.termData }
            employeeMap={ this.employeeMap }
          />

          See an issue or want to add to this website? Fork it or create an issue on

             GitHub
          .

          Made with

           by

            Ryan Hughes

           and UI borrowed from

            Two Factor Authenticaton
          .

    );
  }
}

export default CSSModules(Home, css);
<|endoftext|>


<commit_before>/**
 * -------------------------------------------
 * Decorator for emitting changes
 * -------------------------------------------
 *
 * @example on setter:
 *
 *    class Item {
 *
 *      _label = null
 *      _list = new List()
 *
 *      get label() {
 *        return this._label
 *      }
 *
 *      @emitChange()
 *      set label(val) {
 *        this._label = val
 *      }
 *
 *    }
 *
 * @example on class:
 *
 *    @emitChange('album')
 *    @emitChange('label', 'untitled', [
 *      {
 *        validator: v => /\d+/.test(v),
 *        message: 'Not a number'
 *      },
 *      {
 *        validator = v => typeof v === 'string',
 *        message: 'Must be a string'
 *      }
 *    ])
 *
 *    class Song {
 *
 *    }
 */

import { EventEmitter } from 'events'
import List from '../list/list'
import { isFunction } from './is'

/**
 * Setter deco
 *
 * @param   {EventEmitter}  target
 * @param   {string}        key
 * @param   {object}        descriptor
 * @returns {object}
 */
const setter = function(target, key, descriptor) {
  let fn = descriptor.set

  if (!(EventEmitter.prototype.isPrototypeOf(target)) && !EventEmitter.prototype.isPrototypeOf(target.prototype)) {
    throw new Error('@emitter.emitChange can only be applied to event emitters')
  }

  return {
    ...descriptor,
    configurable: true,
    set(val) {
      const toObj = v => {
        return isFunction(this.toObject)
          ? this.toObject()
          : { [key]: v !== undefined ? v : val }
      }

      // get previous value
      let prev = this['_' + key]
      if (prev && typeof prev.toArray === 'function') {
        prev = prev.toArray()
      } else if (prev && typeof prev.toObject === 'function') {
        prev = prev.toObject()
      }

      let previous

      if (prev !== val) {
        previous = toObj(prev)
      }

      // call class setter method
      fn.call(this, val)

      // is a duplicate on list?
      if (this._list instanceof List && this._list._duplicates !== true) {
        try {
          this._list.checkOnDuplicates()
        } catch (err) {
          fn.call(this, prev)
          throw err
        }
      }

      // only emit changes
      if (prev === val) {
        return
      }

      const changed = { type: key, from: prev, to: val }

      let current
      try {
        current = toObj()
      } catch (err) {
        current = { [key]: val }
      }

      const evtParams = { previous, current, changed }

      const evtChange = ['change', evtParams]
      const evtChangeProp = [`change:${key}`, evtParams, val]

      this.emit(...evtChange)
      this.emit(...evtChangeProp)

      if (this._list && this._list instanceof EventEmitter) {
        this._list.emit(...evtChange)
        this._list.emit(...evtChangeProp)
      }
    }
  }
}

/**
 * Decorator
 *
 * @param {string}  prop          (apply on classes)
 * @param {*}       defaultValue  (optional, default=null)
 * @param {Array}   validators
 */
export function emitChange(prop, defaultValue = null, validators = []) {
  if (prop) {
    // bind as class
    return function(target) {
      // setup class prototype
      Object.defineProperties(target.prototype, {
        [`_${prop}`]: {
          value: defaultValue,
          writable: true,
          enumerable: false,
          configurable: true
        },
        [prop]: {
          configurable: true,
          get() {
            return this[`_${prop}`]
          },
          set(val) {
            const errors = validators.reduce((res, v) => {
              if (!v.validator(val)) {
                res.push(v.message)
              }
              return res
            }, [])

            if (errors.length > 0) {
              throw new Error(`${errors[0]}`)
            }

            this[`_${prop}`] = val
          }
        }
      })

      // apply setter on it
      let descriptor = Object.getOwnPropertyDescriptor(target.prototype, prop)
      Object.defineProperty(target.prototype, prop, setter(target, prop, descriptor))
    }
  }

  // bind as setter
  return setter
}
<commit_msg>Update jsdocs emitter<commit_after>/**
 * -------------------------------------------
 * Decorator for emitting changes
 * -------------------------------------------
 *
 * @example on setter:
 *
 *    class Item {
 *
 *      _label = null
 *      _list = new List()
 *
 *      get label() {
 *        return this._label
 *      }
 *
 *      @emitChange()
 *      set label(val) {
 *        this._label = val
 *      }
 *
 *    }
 *
 * @example on class:
 *
 *    @emitChange('album')
 *    @emitChange('label', 'untitled', [
 *      {
 *        validator: v => /\d+/.test(v),
 *        message: 'Not a number'
 *      },
 *      {
 *        validator = v => typeof v === 'string',
 *        message: 'Must be a string'
 *      }
 *    ])
 *
 *    class Song {
 *
 *    }
 */

import { EventEmitter } from 'events'
import List from '../list/list'
import { isFunction } from './is'

/**
 * Setter deco
 *
 * @param   {EventEmitter}  target
 * @param   {string}        key
 * @param   {object}        descriptor
 * @returns {object}
 */
const setter = function(target, key, descriptor) {
  let fn = descriptor.set

  if (!(EventEmitter.prototype.isPrototypeOf(target)) && !EventEmitter.prototype.isPrototypeOf(target.prototype)) {
    throw new Error('@emitter.emitChange can only be applied to event emitters')
  }

  return {
    ...descriptor,
    configurable: true,
    set(val) {
      const toObj = v => {
        return isFunction(this.toObject)
          ? this.toObject()
          : { [key]: v !== undefined ? v : val }
      }

      // get previous value
      let prev = this['_' + key]
      if (prev && typeof prev.toArray === 'function') {
        prev = prev.toArray()
      } else if (prev && typeof prev.toObject === 'function') {
        prev = prev.toObject()
      }

      let previous

      if (prev !== val) {
        previous = toObj(prev)
      }

      // call class setter method
      fn.call(this, val)

      // is a duplicate on list?
      if (this._list instanceof List && this._list._duplicates !== true) {
        try {
          this._list.checkOnDuplicates()
        } catch (err) {
          fn.call(this, prev)
          throw err
        }
      }

      // only emit changes
      if (prev === val) {
        return
      }

      const changed = { type: key, from: prev, to: val }

      let current
      try {
        current = toObj()
      } catch (err) {
        current = { [key]: val }
      }

      const evtParams = { previous, current, changed }

      const evtChange = ['change', evtParams]
      const evtChangeProp = [`change:${key}`, evtParams, val]

      this.emit(...evtChange)
      this.emit(...evtChangeProp)

      if (this._list && this._list instanceof EventEmitter) {
        this._list.emit(...evtChange)
        this._list.emit(...evtChangeProp)
      }
    }
  }
}

/**
 * Decorator
 *
 * @param {string}  prop          (apply on classes)
 * @param {*}       defaultValue  (optional, default=null)
 * @param {Array}   validators    (optional)
 */
export function emitChange(prop, defaultValue = null, validators = []) {
  if (prop) {
    // bind as class
    return function(target) {
      // setup class prototype
      Object.defineProperties(target.prototype, {
        [`_${prop}`]: {
          value: defaultValue,
          writable: true,
          enumerable: false,
          configurable: true
        },
        [prop]: {
          configurable: true,
          get() {
            return this[`_${prop}`]
          },
          set(val) {
            const errors = validators.reduce((res, v) => {
              if (!v.validator(val)) {
                res.push(v.message)
              }
              return res
            }, [])

            if (errors.length > 0) {
              throw new Error(`${errors[0]}`)
            }

            this[`_${prop}`] = val
          }
        }
      })

      // apply setter on it
      let descriptor = Object.getOwnPropertyDescriptor(target.prototype, prop)
      Object.defineProperty(target.prototype, prop, setter(target, prop, descriptor))
    }
  }

  // bind as setter
  return setter
}
<|endoftext|>


<commit_before>// read local Storage
var savedPhrases = JSON.parse(localStorage.getItem('savedPhrases'));
var phrasesLeft;
var phrasesTotal;
if ($.isArray(savedPhrases)){
	phrasesLeft = savedPhrases.slice();
	phrasesTotal = savedPhrases.slice();
}
else{
	phrasesLeft = [];
	phrasesTotal = [];
}
var savedMin = JSON.parse(localStorage.getItem('minDuration'));
var savedMax = JSON.parse(localStorage.getItem('maxDuration'));
var minDuration;
var maxDuration;
if (savedMin && savedMax){
	minDuration = savedMin;
	maxDuration = savedMax;
}
else{
	minDuration = 0;
	maxDuration = 10;
}

function Timer(){
	// pick random duration after which end the current game
	this.duration = Math.random() * (maxDuration - minDuration) + minDuration;
	console.log(this.duration);
	this.tick = function(){
		this.time = (Date.now() - this.startTime) / 1000;
		$('#timer').html(this.time.toFixed(2) + " seconds");
		$("#reset").show();
		$("#cont").html("Continue");
		if (this.time > this.duration || !(this.ticking)){
			clearInterval(this.interval);
			$("#gameMenu").hide();
			$("#startMenu").show();
			this.ticking = false;
			if (this.time > this.duration){
				$("#log").html("You ran out of time.");
			}
		}
	};
	this.stop = function(){
		this.ticking = false;
	};
	this.start = function(){
		this.startTime = Date.now();
		this.ticking = true;
		this.interval = setInterval(this.tick.bind(this), 10);
	};
}

//callback functions
var uploadFile = function(event) {
    var input = event.target;
    var reader = new FileReader();
    reader.onload = function(){
    	var text = reader.result;
    	phrasesLeft = phrasesLeft.concat(text.split("\n"));
    	phrasesTotal = phrasesTotal.concat(text.split("\n"));
    	localStorage.setItem('savedPhrases', JSON.stringify(phrasesTotal));
    };
    reader.readAsText(input.files[0]);
    $("#phrases").html(phrasesTotal.reverse().slice(0,20).join("
"));
};

var timer;
var cont = function(){
	$("#startMenu").hide();
	$("#gameMenu").show();
	timer = new Timer();
	timer.start();
	next();
};

var next = function(){
	if (phrasesLeft.length === 0){
		$("#log").html("BOOM! You ran out of catchphrases!");
		$("#cont").hide();
		timer.stop();
	}
	var idx = Math.floor(Math.random() * phrasesLeft.length);
	var nextPhrase = phrasesLeft.splice(idx, 1)[0];
	$("#phrase").html(nextPhrase);
};

var reset = function(){
	phrasesLeft = phrasesTotal.slice();
	$("#reset").hide();
	$("#cont").html("Play");
	$("#cont").show();
};

// listen for button clicks and keypresses
$(document).ready(function(){
	reset();
	console.log("Hello, nerds!");
	 $("#phrases").html(phrasesTotal.reverse().slice(0,20).join("
"));
	$("#gameMenu").hide();
	$("#reset").hide();
	$("#cont").click(cont);
	$("#next").click(next);
	$("#reset").click(reset);
	$("#newPhrase").keyup(function(key){
		if (key.which === 13){
			// Carriage return
			var newPhrase = document.getElementById("newPhrase").value;
			document.getElementById("newPhrase").value = "";
			phrasesTotal.push(newPhrase);
			phrasesLeft.push(newPhrase);
			localStorage.setItem('savedPhrases', JSON.stringify(phrasesTotal));
			 $("#phrases").html(phrasesTotal.reverse().slice(0,20).join("
"));
		}
	});
	$("#durationRange").slider({
        range: true,
      	min: 0,
        max: 10,
        values: [minDuration, maxDuration],
        slide: function(event, ui) {
      	    minDuration = ui.values[0];
            maxDuration = ui.values[1];
            localStorage.setItem('minDuration', JSON.stringify(minDuration));
            localStorage.setItem('maxDuration', JSON.stringify(maxDuration));
            $("#amount").val(minDuration + "s - " + maxDuration + "s");
            showGraph();
      }
    });
    $( "#amount" ).val($( "#durationRange" ).slider( "values", 0 ) + "s - " +
    			       $( "#durationRange" ).slider( "values", 1 ) + "s" );
    showGraph();
});

var showGraph = function(){
    d3.select("svg").remove();
    var margin = {top: 50, right: 50, bottom: 50, left: 70},
    width = $("#settings").width() - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
    var x = d3.scale.linear()
        .range([0, width]);
    var y = d3.scale.linear()
        .range([height, 0]);
    var xAxis = d3.svg.axis()
        .scale(x)
        .orient("bottom");
    var yAxis = d3.svg.axis()
        .scale(y)
        .orient("left");
    var line = d3.svg.line()
        .x(function(d) { return x(d.date); })
        .y(function(d) { return y(d.close); });
    var svg = d3.select("#graph").append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    var data = [{"date": 0, "close": 93.24}, {"date": 100, "close": 95.35}, {"date": 200, "close": 98.84}];
    x.domain(d3.extent(data, function(d) { return d.date; }));
    y.domain(d3.extent(data, function(d) { return d.close; }));
    svg.append("g")
        .attr("class", "x axis")
        .call(xAxis)
        .attr("transform", "translate(0," + height + ")")
        .append("text")
        .attr("transform", "translate(" + (width/2) + ",40)")
        .attr("x", 6)
        .attr("dx", ".71em")
        .style("text-anchor", "start")
        .text("Duration");
    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text")
        .attr("transform", "rotate(-90) translate(-" + (height/2),20)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("Probability density");
    svg.append("path")
        .datum(data)
        .attr("class", "line")
        .attr("d", line);
};

// math
var sgn = function(x){
	if(x === 0){
        return 0;
    }
    else if(x > 0){
        return 1;
    }
    else {
        return -1;
    }
};

var sqrd = function(x){
	return x * x;
};

var normpdf = function(x, a, b, mu, sigma){
	if (a <= x && x <= b){
		return Math.exp(- sqrd(Math.pow((x - mu) / sigma)) / 2) / (Math.sqrt(2 * Math.PI) * sigma);
	}
	else{
		return 0;
	}
};

var erfinv = function(x){
    var a  = 0.147;
    if(x === 0){
    	return 0;
    }
    else {
        var ln = Math.log(1-x*x);
        var firstSqrt = Math.sqrt(sqrd(2 /(Math.PI * a) + ln /2) - ln / a );
        var secondSqrt = Math.sqrt(firstSqrt - (2 / (Math.PI * a) + ln / 2));
        return sgn(x) * secondSqrt;
    }
};

var probit = function(x){
	return Math.sqrt(2) * erfinv(2 * x -1);
};

var normrv = function(a, b, mu, sigma){
	if (a >= b){
		throw "Nonsensical range";
	}
	while (true){
		var rv = mu + sigma * probit(Math.random());
		if (a <= rv && rv <= b){
			return rv;
		}
	}
};
<commit_msg>fixed mismatched inverted commas<commit_after>// read local Storage
var savedPhrases = JSON.parse(localStorage.getItem('savedPhrases'));
var phrasesLeft;
var phrasesTotal;
if ($.isArray(savedPhrases)){
	phrasesLeft = savedPhrases.slice();
	phrasesTotal = savedPhrases.slice();
}
else{
	phrasesLeft = [];
	phrasesTotal = [];
}
var savedMin = JSON.parse(localStorage.getItem('minDuration'));
var savedMax = JSON.parse(localStorage.getItem('maxDuration'));
var minDuration;
var maxDuration;
if (savedMin && savedMax){
	minDuration = savedMin;
	maxDuration = savedMax;
}
else{
	minDuration = 0;
	maxDuration = 10;
}

function Timer(){
	// pick random duration after which end the current game
	this.duration = Math.random() * (maxDuration - minDuration) + minDuration;
	console.log(this.duration);
	this.tick = function(){
		this.time = (Date.now() - this.startTime) / 1000;
		$('#timer').html(this.time.toFixed(2) + " seconds");
		$("#reset").show();
		$("#cont").html("Continue");
		if (this.time > this.duration || !(this.ticking)){
			clearInterval(this.interval);
			$("#gameMenu").hide();
			$("#startMenu").show();
			this.ticking = false;
			if (this.time > this.duration){
				$("#log").html("You ran out of time.");
			}
		}
	};
	this.stop = function(){
		this.ticking = false;
	};
	this.start = function(){
		this.startTime = Date.now();
		this.ticking = true;
		this.interval = setInterval(this.tick.bind(this), 10);
	};
}

//callback functions
var uploadFile = function(event) {
    var input = event.target;
    var reader = new FileReader();
    reader.onload = function(){
    	var text = reader.result;
    	phrasesLeft = phrasesLeft.concat(text.split("\n"));
    	phrasesTotal = phrasesTotal.concat(text.split("\n"));
    	localStorage.setItem('savedPhrases', JSON.stringify(phrasesTotal));
    };
    reader.readAsText(input.files[0]);
    $("#phrases").html(phrasesTotal.reverse().slice(0,20).join("
"));
};

var timer;
var cont = function(){
	$("#startMenu").hide();
	$("#gameMenu").show();
	timer = new Timer();
	timer.start();
	next();
};

var next = function(){
	if (phrasesLeft.length === 0){
		$("#log").html("BOOM! You ran out of catchphrases!");
		$("#cont").hide();
		timer.stop();
	}
	var idx = Math.floor(Math.random() * phrasesLeft.length);
	var nextPhrase = phrasesLeft.splice(idx, 1)[0];
	$("#phrase").html(nextPhrase);
};

var reset = function(){
	phrasesLeft = phrasesTotal.slice();
	$("#reset").hide();
	$("#cont").html("Play");
	$("#cont").show();
};

// listen for button clicks and keypresses
$(document).ready(function(){
	reset();
	console.log("Hello, nerds!");
	 $("#phrases").html(phrasesTotal.reverse().slice(0,20).join("
"));
	$("#gameMenu").hide();
	$("#reset").hide();
	$("#cont").click(cont);
	$("#next").click(next);
	$("#reset").click(reset);
	$("#newPhrase").keyup(function(key){
		if (key.which === 13){
			// Carriage return
			var newPhrase = document.getElementById("newPhrase").value;
			document.getElementById("newPhrase").value = "";
			phrasesTotal.push(newPhrase);
			phrasesLeft.push(newPhrase);
			localStorage.setItem('savedPhrases', JSON.stringify(phrasesTotal));
			 $("#phrases").html(phrasesTotal.reverse().slice(0,20).join("
"));
		}
	});
	$("#durationRange").slider({
        range: true,
      	min: 0,
        max: 10,
        values: [minDuration, maxDuration],
        slide: function(event, ui) {
      	    minDuration = ui.values[0];
            maxDuration = ui.values[1];
            localStorage.setItem('minDuration', JSON.stringify(minDuration));
            localStorage.setItem('maxDuration', JSON.stringify(maxDuration));
            $("#amount").val(minDuration + "s - " + maxDuration + "s");
            showGraph();
      }
    });
    $( "#amount" ).val($( "#durationRange" ).slider( "values", 0 ) + "s - " +
    			       $( "#durationRange" ).slider( "values", 1 ) + "s" );
    showGraph();
});

var showGraph = function(){
    d3.select("svg").remove();
    var margin = {top: 50, right: 50, bottom: 50, left: 70},
    width = $("#settings").width() - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
    var x = d3.scale.linear()
        .range([0, width]);
    var y = d3.scale.linear()
        .range([height, 0]);
    var xAxis = d3.svg.axis()
        .scale(x)
        .orient("bottom");
    var yAxis = d3.svg.axis()
        .scale(y)
        .orient("left");
    var line = d3.svg.line()
        .x(function(d) { return x(d.date); })
        .y(function(d) { return y(d.close); });
    var svg = d3.select("#graph").append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    var data = [{"date": 0, "close": 93.24}, {"date": 100, "close": 95.35}, {"date": 200, "close": 98.84}];
    x.domain(d3.extent(data, function(d) { return d.date; }));
    y.domain(d3.extent(data, function(d) { return d.close; }));
    svg.append("g")
        .attr("class", "x axis")
        .call(xAxis)
        .attr("transform", "translate(0," + height + ")")
        .append("text")
        .attr("transform", "translate(" + (width/2) + ",40)")
        .attr("x", 6)
        .attr("dx", ".71em")
        .style("text-anchor", "start")
        .text("Duration");
    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text")
        .attr("transform", "rotate(-90) translate(-" + (height/2) + ",20)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("Probability density");
    svg.append("path")
        .datum(data)
        .attr("class", "line")
        .attr("d", line);
};

// math
var sgn = function(x){
	if(x === 0){
        return 0;
    }
    else if(x > 0){
        return 1;
    }
    else {
        return -1;
    }
};

var sqrd = function(x){
	return x * x;
};

var normpdf = function(x, a, b, mu, sigma){
	if (a <= x && x <= b){
		return Math.exp(- sqrd(Math.pow((x - mu) / sigma)) / 2) / (Math.sqrt(2 * Math.PI) * sigma);
	}
	else{
		return 0;
	}
};

var erfinv = function(x){
    var a  = 0.147;
    if(x === 0){
    	return 0;
    }
    else {
        var ln = Math.log(1-x*x);
        var firstSqrt = Math.sqrt(sqrd(2 /(Math.PI * a) + ln /2) - ln / a );
        var secondSqrt = Math.sqrt(firstSqrt - (2 / (Math.PI * a) + ln / 2));
        return sgn(x) * secondSqrt;
    }
};

var probit = function(x){
	return Math.sqrt(2) * erfinv(2 * x -1);
};

var normrv = function(a, b, mu, sigma){
	if (a >= b){
		throw "Nonsensical range";
	}
	while (true){
		var rv = mu + sigma * probit(Math.random());
		if (a <= rv && rv <= b){
			return rv;
		}
	}
};
<|endoftext|>


<commit_before>lingcod.rest = {}

lingcod.rest.client = function(gex, panel, manipulators){
    var that;

    if(!gex){
        throw('earth-api-utility-library instance required.');
    }

    if(!panel){
        throw('lingcod.panel instance required.');
    }

    that = {};

    var processLocation = function(location){
        var mydomain=window.location.href.match(/:\/\/(.[^/]+)/)[1];
        if(location.search(mydomain) !== -1){
            location = location.split(mydomain)[1];
        }
        return location;
    }

    // Reads a kml document (from a string) and returns an array of objects
    // that can be used as input to client.create().
    var parseDocument = function(kml){
        var xml = $(kml);
        var return_values = {};
        xml.find("atom\\:link[rel='marinemap.create_form']").each(function(){
            $link = $(this);
            return_values[$link.attr('mm:model')] = {
                model: $link.attr('mm:model'),
                href: $link.attr('href'),
                icon: $link.attr('mm:icon'),
                title: $link.attr('title')
            }
        });
        return return_values;
    };

    that.parseDocument = parseDocument;

    var parseResource = function(kmlFeatureObject){
        var kml = kmlFeatureObject.getKml();
        var self = $(kml).find('atom\\:link[rel=self]');
        var form = $(kml).find('atom\\:link[rel=marinemap.update_form]');
        var share_form = $(kml).find('atom\\:link[rel=marinemap.share_form]');
        if(self.length === 1 && form.length === 1){
            var resource = {
                title: self.attr('title'),
                location: self.attr('href'),
                model: self.attr('mm:model'),
                form_icon: self.attr('mm:icon'),
                form_link: form.attr('href'),
                form_title: form.attr('title')
            }
            if (share_form.length === 1) {
                resource['share_href'] = share_form.attr('href');
                resource['share_title'] = share_form.attr('title');
                resource['share_model'] = share_form.attr('mm:model');
            }
            return resource;
        }else{
            throw('REST Client: Could not parse resource.');
        }
    };

    that.parseResource = parseResource;

    var onsubmit = function(e, form, options, config){
        var action = $(form).attr('action');
        panel.close();
        $.ajax({
            url: action,
            type: 'POST',
            data: $(form).serialize(),
            complete: function(req, status){
                switch(req.status){
                    case 201:
                        // new object created, get location header
                        if(options && options.success){
                            options.success(
                                processLocation(req.getResponseHeader('Location')));
                        }else{
                            if(options.error){
                                options.error();
                            }
                        }
                        break;

                    case 200:
                        // object edited successfully
                        if(options.success){
                            options.success(processLocation(options.location));
                        }else{
                            if(options.error){
                                options.error();
                            }
                        }
                        break;

                    case 400:
                        // validation error
                        options['validation_error'] = true;
                        setupForm(req.responseText, options, config);
                        break;

                    default:
                        if(options.error){
                            options.error();
                        }
                }
            }
        });
        // on error
        // panel.showError('title', 'msg');
        // if(options && options['error']){
        //     error(title, msg);
        // }
    };

    var setupForm = function(text, options, config){
        var content = $('

Geometry

Attributes

SubmitCancel

');
        var html = $(text);
        var h1 = html.find('h1');
        h1.remove();
        content.prepend(h1);
        html.find('input[type=submit]').hide();
        var form = html.find('form');
        content.find('#PanelAttributes').append(html);
        panel.addContent(content);
        // TODO
        var el = panel.getEl();
        var tabs = content.find('.tabs').tabs();
        tabs.bind('tabsshow', function(e){
            var div = $(this).parent().parent().parent();
            // scroll to 1, then 0 for the benefit of dumb firefox
            div.scrollTop(1);
            div.scrollTop(0);
        });
        // so this is how it might work:
        // var manipulations_needed = manipulators.needed(form);
        var manipulator = new lingcod.Manipulator(gex, html.find('form'), $('#PanelGeometry'), $('#map_container'));
        if(manipulator && manipulator.needed){
            tabs.tabs('select', '#PanelGeometry');
        }else{
            manipulator = false;
            tabs.tabs('select', '#PanelAttributes');
            tabs.tabs('disable', 0);
            tabs.find('> .ui-tabs-nav').hide();
        }
        el.find('form').submit(function(e){
            if(manipulator){
                var errMsg = false;
                if(manipulator.isDefiningShape()){
                    if(manipulator.isInvalidGeometry()){
                        errMsg = 'The shape you defined is invalid. Please correct any mistakes using the Geometry form.';
                    }else if(manipulator.isDefiningNewShape()){
                        errMsg = 'You must finish defining your shape before creating this feature. Double-Click on the last vertex to finish drawing your shape.';
                    }else{
                        errMsg = 'You must finish defining your shape before creating this feature. Click "Done Editing", when you are finished';
                    }
                }else if(manipulator.isShapeDefined() === false){
                    errMsg = 'You must create a geometry for this feature before continuing. Click on "Draw Shape" to begin.';
                }
                if(errMsg){
                    tabs.tabs('select', '#PanelGeometry');
                    alert(errMsg);
                    return false;
                }else{
                    // can proceed with form submission
                    manipulator.destroy();
                }
            }
            onsubmit(e, form, options, config);
            return false;
        });
        el.find('.submit_button').click(function(){
            form.trigger('submit');
        });
        el.find('.cancel_button').click(function(){
            if(manipulator){
                manipulator.destroy();
            }
            panel.close();
            if(options.cancel){
                options.cancel();
            }
        });
        if(options.validation_error){
            tabs.tabs('select', '#PanelAttributes');
            $('#PanelAttributes').parent().parent().parent().parent().scrollTop(1).scrollTop(0);
        }
        panel.show();
        $('#PanelAttributes').scrollTop(1).scrollTop(0);

        $(that).trigger('form_shown', [panel, config.model]);
    };

    var create = function(config, options){
        var self = this;
        options = options || {};
        $.ajax({
            cache: false,
            url: config.href,
            type: 'GET',
            success: function(data, status){
                if(status === 'success'){
                    setupForm(data, options, config);
                }else{
                    alert('Could not retrieve form. Your computer was unable to contact the server.');
                    if(options.error){
                        options.error();
                    }
                }
            },
            error: function(e, b){
                alert('Could not retrieve form. Your computer was unable to contact the server.');
                if(options.error){
                    options.error();
                }
            }
        });
    };

    that.create = create;

    var getConfig = function(configOrFeature){
        if((typeof configOrFeature === 'object' || typeof configOrFeature === 'function') && configOrFeature.getType){
            return parseResource(configOrFeature);
        }else{
            return configOrFeature;
        }
    };

    var update = function(configOrFeature, options){
        var config = getConfig(configOrFeature);
        var options = options || {};
        options.location = config.location;
        $.ajax({
            cache: false,
            url: config.form_link,
            type: 'GET',
            success: function(data, status){
                if(status === 'success'){
                    setupForm(data, options, config);
                }else{
                    alert('Could not retrieve form. Your computer was unable to contact the server.');
                    if(options.error){
                        options.error();
                    }
                }
            },
            error: function(e, b){
                if(options && options.error){
                    alert('Could not retrieve form. Your computer was unable to contact the server.');
                    options.error();
                }
            }
        });
    };

    that.update = update;

    var destroy = function(configOrFeature, options){
        var config = getConfig(configOrFeature);
        var options = options || {};
        var answer;
        if(options.confirm !== false){
            answer = confirm(
                'Are you sure you want to delete "'+config.title+'"?');
        }else{
            answer = true;
        }
        if(answer){
            $.ajax({
                url: config.location,
                type: 'DELETE',
                complete: function(response, status){
                    if(status === 'success'){
                        if(options.success){
                            options.success(processLocation(config.location));
                        }
                    }else{
                        // show an error
                        panel.showError('Server Error', 'Could not delete.');
                        if(options.error){
                            options.error(response, status);
                        }
                    }
                }
            });
        }else{
            if(options.cancel){
                options.cancel(processLocation(config.location));
            }
        }
    };

    that.destroy = destroy;

    // client.show
    // ===========
    // Show a feature's attributes in the panel.
    //
    // Returns a config object for the passed feature.
    //
    // @param {KmlFeatureObject or Object} configOrFeature, the feature to show
    // or a config object for a feature.
    //
    // @param {Object} options, an object with optional success and error
    // callbacks.
    //
    // Usage:
    //      var config = client.show(kmlFeatureObject, {
    //          success: function(location){
    //              do something
    //          },
    //          error: function(response, status){
    //              do something
    //          }
    //      });
    //
    // now I can call client.show with config:
    //      client.show(config, options);
    //
    var show = function(configOrFeature, options){
        options = options || {};
        var config = getConfig(configOrFeature);
        options['load_msg'] = 'Loading '+config['title'];
        options['showClose'] = true;
        panel.showUrl(config['location'], options);
    };

    that.show = show;

    // TODO client.share
    // ===========
    // Show a feature's sharing UI in the panel.
    //
    // Override the forms behavior to make ajax calls
    // and place returned html in the panel
    var share = function(configOrFeature, options){
        var config = getConfig(configOrFeature);
        var action = config['share_href'];
        options = options || {};
        options['load_msg'] = 'Loading '+config['title'];
        options['showClose'] = true;

        panel.showUrl(config['share_href'], {
            success: function(){
                var element = panel.getEl();
                var button_html = [
                    '
',
                    '
',
                        '',
                            'Submit',
                        '',
                        '',
                            'Cancel',
                        '',
                        '
',
                    '
',
                ].join('');

                var form = element.find('form');
                form.after(button_html);
                form.submit( function(e) {
                    var formdata = form.serialize();
                    $.ajax({
                        url: action,
                        type: 'POST',
                        data: formdata,
                        success: function(data,textStatus){
                            panel.showContent(data, options);
                        },
                        error: function(xhr, textStatus, errorThrown){
                            alert('There was a problem posting your data to the server.');
                        }
                    });
                    e.preventDefault();
                });
                element.find('.submit_button').click(function(){
                    form.trigger('submit');
                });
                element.find('.cancel_button').click(function(){
                    panel.close();
                });
            },
            error: function(req,b) {
                if (req.status == 404) {
                    alert(req.responseText);
                } else {
                    alert('There was a problem getting the sharing form. Please try again; if the problem persists, please contact us.');
                }
            }
        });
    };

    that.share = share;

    // Private methods

    // to make up for the fact that the Location header returns the full path,
    // and get_absolute_url calls for each resource in the kml will not
    // actually return an absolute url.
    // See http://code.djangoproject.com/wiki/ReplacingGetAbsoluteUrl
    function getPath(url) {
        return $('').attr('href',url)[0].pathname.replace(/^[^\/]/,'/');
    };

    that.getPath = getPath;

    // METHODS TO AID IN TESTING
    // =========================

    // Inefficient. Really only for testing
    // Finds Kml Feature within a text file that contains a Feature with
    // matching location.
    var findResourceInString = function(location, text){
        var path = getPath(location);
        var link = $(text).find('atom\\:link[href='+path+']');
        if(link.length === 0){
            var link = $(text).find('atom\\:link[href='+location+']');
        }
        if(link.length){
           return link.parent();
        }else{
            return false;
        }
    };

    that.findResourceInString = findResourceInString;

    // Inefficient, only for testing.
    // Given the location of a resource and a kmlObject that contains it,
    // find the KmlFeatureObject that represents it.
    var findResource = function(location, kmlObject){
        var resource = false;
        gex.dom.walk({
            rootObject: kmlObject,
            visitCallback: function(context){
                var text = this.getKml();
                var result = findResourceInString(location, text);
                if(result){
                    resource = this;
                    // don't return false. We have to continue deeper to make
                    // sure we're not selecting a container element.
                }
            }
        });
        return resource;
    };

    that.findResource = findResource;

    return that;
};
<commit_msg>MPA attributes entry form now scrolls to the top after getting a validation error. [my last checkin didn't work consistently]<commit_after>lingcod.rest = {}

lingcod.rest.client = function(gex, panel, manipulators){
    var that;

    if(!gex){
        throw('earth-api-utility-library instance required.');
    }

    if(!panel){
        throw('lingcod.panel instance required.');
    }

    that = {};

    var processLocation = function(location){
        var mydomain=window.location.href.match(/:\/\/(.[^/]+)/)[1];
        if(location.search(mydomain) !== -1){
            location = location.split(mydomain)[1];
        }
        return location;
    }

    // Reads a kml document (from a string) and returns an array of objects
    // that can be used as input to client.create().
    var parseDocument = function(kml){
        var xml = $(kml);
        var return_values = {};
        xml.find("atom\\:link[rel='marinemap.create_form']").each(function(){
            $link = $(this);
            return_values[$link.attr('mm:model')] = {
                model: $link.attr('mm:model'),
                href: $link.attr('href'),
                icon: $link.attr('mm:icon'),
                title: $link.attr('title')
            }
        });
        return return_values;
    };

    that.parseDocument = parseDocument;

    var parseResource = function(kmlFeatureObject){
        var kml = kmlFeatureObject.getKml();
        var self = $(kml).find('atom\\:link[rel=self]');
        var form = $(kml).find('atom\\:link[rel=marinemap.update_form]');
        var share_form = $(kml).find('atom\\:link[rel=marinemap.share_form]');
        if(self.length === 1 && form.length === 1){
            var resource = {
                title: self.attr('title'),
                location: self.attr('href'),
                model: self.attr('mm:model'),
                form_icon: self.attr('mm:icon'),
                form_link: form.attr('href'),
                form_title: form.attr('title')
            }
            if (share_form.length === 1) {
                resource['share_href'] = share_form.attr('href');
                resource['share_title'] = share_form.attr('title');
                resource['share_model'] = share_form.attr('mm:model');
            }
            return resource;
        }else{
            throw('REST Client: Could not parse resource.');
        }
    };

    that.parseResource = parseResource;

    var onsubmit = function(e, form, options, config){
        var action = $(form).attr('action');
        panel.close();
        $.ajax({
            url: action,
            type: 'POST',
            data: $(form).serialize(),
            complete: function(req, status){
                switch(req.status){
                    case 201:
                        // new object created, get location header
                        if(options && options.success){
                            options.success(
                                processLocation(req.getResponseHeader('Location')));
                        }else{
                            if(options.error){
                                options.error();
                            }
                        }
                        break;

                    case 200:
                        // object edited successfully
                        if(options.success){
                            options.success(processLocation(options.location));
                        }else{
                            if(options.error){
                                options.error();
                            }
                        }
                        break;

                    case 400:
                        // validation error
                        options['validation_error'] = true;
                        setupForm(req.responseText, options, config);
                        break;

                    default:
                        if(options.error){
                            options.error();
                        }
                }
            }
        });
        // on error
        // panel.showError('title', 'msg');
        // if(options && options['error']){
        //     error(title, msg);
        // }
    };

    var setupForm = function(text, options, config){
        var content = $('

Geometry

Attributes

SubmitCancel

');
        var html = $(text);
        var h1 = html.find('h1');
        h1.remove();
        content.prepend(h1);
        html.find('input[type=submit]').hide();
        var form = html.find('form');
        content.find('#PanelAttributes').append(html);
        panel.addContent(content);
        // TODO
        var el = panel.getEl();
        var tabs = content.find('.tabs').tabs();
        tabs.bind('tabsshow', function(e){
            var div = $(this).parent().parent().parent();
            // scroll to 1, then 0 for the benefit of dumb firefox
            div.scrollTop(1);
            div.scrollTop(0);
        });
        // so this is how it might work:
        // var manipulations_needed = manipulators.needed(form);
        var manipulator = new lingcod.Manipulator(gex, html.find('form'), $('#PanelGeometry'), $('#map_container'));
        if(manipulator && manipulator.needed){
            tabs.tabs('select', '#PanelGeometry');
        }else{
            manipulator = false;
            tabs.tabs('select', '#PanelAttributes');
            tabs.tabs('disable', 0);
            tabs.find('> .ui-tabs-nav').hide();
        }
        el.find('form').submit(function(e){
            if(manipulator){
                var errMsg = false;
                if(manipulator.isDefiningShape()){
                    if(manipulator.isInvalidGeometry()){
                        errMsg = 'The shape you defined is invalid. Please correct any mistakes using the Geometry form.';
                    }else if(manipulator.isDefiningNewShape()){
                        errMsg = 'You must finish defining your shape before creating this feature. Double-Click on the last vertex to finish drawing your shape.';
                    }else{
                        errMsg = 'You must finish defining your shape before creating this feature. Click "Done Editing", when you are finished';
                    }
                }else if(manipulator.isShapeDefined() === false){
                    errMsg = 'You must create a geometry for this feature before continuing. Click on "Draw Shape" to begin.';
                }
                if(errMsg){
                    tabs.tabs('select', '#PanelGeometry');
                    alert(errMsg);
                    return false;
                }else{
                    // can proceed with form submission
                    manipulator.destroy();
                }
            }
            onsubmit(e, form, options, config);
            return false;
        });
        el.find('.submit_button').click(function(){
            form.trigger('submit');
        });
        el.find('.cancel_button').click(function(){
            if(manipulator){
                manipulator.destroy();
            }
            panel.close();
            if(options.cancel){
                options.cancel();
            }
        });
        if(options.validation_error){
            tabs.tabs('select', '#PanelAttributes');
        }
        panel.show();
        $('#PanelAttributes').parent().parent().parent().parent().scrollTop(1).scrollTop(0);

        $(that).trigger('form_shown', [panel, config.model]);
    };

    var create = function(config, options){
        var self = this;
        options = options || {};
        $.ajax({
            cache: false,
            url: config.href,
            type: 'GET',
            success: function(data, status){
                if(status === 'success'){
                    setupForm(data, options, config);
                }else{
                    alert('Could not retrieve form. Your computer was unable to contact the server.');
                    if(options.error){
                        options.error();
                    }
                }
            },
            error: function(e, b){
                alert('Could not retrieve form. Your computer was unable to contact the server.');
                if(options.error){
                    options.error();
                }
            }
        });
    };

    that.create = create;

    var getConfig = function(configOrFeature){
        if((typeof configOrFeature === 'object' || typeof configOrFeature === 'function') && configOrFeature.getType){
            return parseResource(configOrFeature);
        }else{
            return configOrFeature;
        }
    };

    var update = function(configOrFeature, options){
        var config = getConfig(configOrFeature);
        var options = options || {};
        options.location = config.location;
        $.ajax({
            cache: false,
            url: config.form_link,
            type: 'GET',
            success: function(data, status){
                if(status === 'success'){
                    setupForm(data, options, config);
                }else{
                    alert('Could not retrieve form. Your computer was unable to contact the server.');
                    if(options.error){
                        options.error();
                    }
                }
            },
            error: function(e, b){
                if(options && options.error){
                    alert('Could not retrieve form. Your computer was unable to contact the server.');
                    options.error();
                }
            }
        });
    };

    that.update = update;

    var destroy = function(configOrFeature, options){
        var config = getConfig(configOrFeature);
        var options = options || {};
        var answer;
        if(options.confirm !== false){
            answer = confirm(
                'Are you sure you want to delete "'+config.title+'"?');
        }else{
            answer = true;
        }
        if(answer){
            $.ajax({
                url: config.location,
                type: 'DELETE',
                complete: function(response, status){
                    if(status === 'success'){
                        if(options.success){
                            options.success(processLocation(config.location));
                        }
                    }else{
                        // show an error
                        panel.showError('Server Error', 'Could not delete.');
                        if(options.error){
                            options.error(response, status);
                        }
                    }
                }
            });
        }else{
            if(options.cancel){
                options.cancel(processLocation(config.location));
            }
        }
    };

    that.destroy = destroy;

    // client.show
    // ===========
    // Show a feature's attributes in the panel.
    //
    // Returns a config object for the passed feature.
    //
    // @param {KmlFeatureObject or Object} configOrFeature, the feature to show
    // or a config object for a feature.
    //
    // @param {Object} options, an object with optional success and error
    // callbacks.
    //
    // Usage:
    //      var config = client.show(kmlFeatureObject, {
    //          success: function(location){
    //              do something
    //          },
    //          error: function(response, status){
    //              do something
    //          }
    //      });
    //
    // now I can call client.show with config:
    //      client.show(config, options);
    //
    var show = function(configOrFeature, options){
        options = options || {};
        var config = getConfig(configOrFeature);
        options['load_msg'] = 'Loading '+config['title'];
        options['showClose'] = true;
        panel.showUrl(config['location'], options);
    };

    that.show = show;

    // TODO client.share
    // ===========
    // Show a feature's sharing UI in the panel.
    //
    // Override the forms behavior to make ajax calls
    // and place returned html in the panel
    var share = function(configOrFeature, options){
        var config = getConfig(configOrFeature);
        var action = config['share_href'];
        options = options || {};
        options['load_msg'] = 'Loading '+config['title'];
        options['showClose'] = true;

        panel.showUrl(config['share_href'], {
            success: function(){
                var element = panel.getEl();
                var button_html = [
                    '
',
                    '
',
                        '',
                            'Submit',
                        '',
                        '',
                            'Cancel',
                        '',
                        '
',
                    '
',
                ].join('');

                var form = element.find('form');
                form.after(button_html);
                form.submit( function(e) {
                    var formdata = form.serialize();
                    $.ajax({
                        url: action,
                        type: 'POST',
                        data: formdata,
                        success: function(data,textStatus){
                            panel.showContent(data, options);
                        },
                        error: function(xhr, textStatus, errorThrown){
                            alert('There was a problem posting your data to the server.');
                        }
                    });
                    e.preventDefault();
                });
                element.find('.submit_button').click(function(){
                    form.trigger('submit');
                });
                element.find('.cancel_button').click(function(){
                    panel.close();
                });
            },
            error: function(req,b) {
                if (req.status == 404) {
                    alert(req.responseText);
                } else {
                    alert('There was a problem getting the sharing form. Please try again; if the problem persists, please contact us.');
                }
            }
        });
    };

    that.share = share;

    // Private methods

    // to make up for the fact that the Location header returns the full path,
    // and get_absolute_url calls for each resource in the kml will not
    // actually return an absolute url.
    // See http://code.djangoproject.com/wiki/ReplacingGetAbsoluteUrl
    function getPath(url) {
        return $('').attr('href',url)[0].pathname.replace(/^[^\/]/,'/');
    };

    that.getPath = getPath;

    // METHODS TO AID IN TESTING
    // =========================

    // Inefficient. Really only for testing
    // Finds Kml Feature within a text file that contains a Feature with
    // matching location.
    var findResourceInString = function(location, text){
        var path = getPath(location);
        var link = $(text).find('atom\\:link[href='+path+']');
        if(link.length === 0){
            var link = $(text).find('atom\\:link[href='+location+']');
        }
        if(link.length){
           return link.parent();
        }else{
            return false;
        }
    };

    that.findResourceInString = findResourceInString;

    // Inefficient, only for testing.
    // Given the location of a resource and a kmlObject that contains it,
    // find the KmlFeatureObject that represents it.
    var findResource = function(location, kmlObject){
        var resource = false;
        gex.dom.walk({
            rootObject: kmlObject,
            visitCallback: function(context){
                var text = this.getKml();
                var result = findResourceInString(location, text);
                if(result){
                    resource = this;
                    // don't return false. We have to continue deeper to make
                    // sure we're not selecting a container element.
                }
            }
        });
        return resource;
    };

    that.findResource = findResource;

    return that;
};
<|endoftext|>


<commit_before>import './index.css';
import 'skatejs-named-slots/src/index';
import kickflip, { emit, link } from '../../src/index';
import create, { button, form, input, p, slot, text } from '../../src/vdom';

const Xtodo = kickflip('x-todo', {
  events: {
    'todo-add' (e) {
      this.items = this.items.concat(e.detail);
      this.value = '';
    },
    'todo-remove' (e) {
      this.items = this.items.filter(item => item !== e.detail);
    }
  },
  properties: {
    items: {
      attribute: false,
      default () { return ['default item']; }
    },
    value: {
      default: ''
    }
  },
  render (elem) {
    create('x-list', { onchange: link(elem), value: elem.value }, function () {
      elem.items.forEach(function (val, key) {
        create('x-item', { key }, val);
      });
    });
  }
});

const Xlist = kickflip('x-list', {
  events: {
    submit (e) {
      e.preventDefault();
      emit(this, 'todo-add', { detail: this.value });
    }
  },
  properties: {
    value: {
      default: '',
      event: 'change'
    }
  },
  render (elem) {
    form(function () {
      input({ onkeyup: link(elem), value: elem.value });
      button({ type: 'submit' }, 'add');
    });
    slot(function () {
      p('There is nothing to do.');
    });
  }
});

const Xitem = kickflip('x-item', {
  events: {
    'click button' () {
      emit(this, 'todo-remove', { detail: this.textContent });
    }
  },
  render () {
    slot();
    text(' ');
    button('remove');
  }
});
<commit_msg>Correct import for demo so browserify doesn't spit out errors.<commit_after>import './index.css';
import 'skatejs-named-slots';
import kickflip, { emit, link } from '../../src/index';
import create, { button, form, input, p, slot, text } from '../../src/vdom';

const Xtodo = kickflip('x-todo', {
  events: {
    'todo-add' (e) {
      this.items = this.items.concat(e.detail);
      this.value = '';
    },
    'todo-remove' (e) {
      this.items = this.items.filter(item => item !== e.detail);
    }
  },
  properties: {
    items: {
      attribute: false,
      default () { return ['default item']; }
    },
    value: {
      default: ''
    }
  },
  render (elem) {
    create('x-list', { onchange: link(elem), value: elem.value }, function () {
      elem.items.forEach(function (val, key) {
        create('x-item', { key }, val);
      });
    });
  }
});

const Xlist = kickflip('x-list', {
  events: {
    submit (e) {
      e.preventDefault();
      emit(this, 'todo-add', { detail: this.value });
    }
  },
  properties: {
    value: {
      default: '',
      event: 'change'
    }
  },
  render (elem) {
    form(function () {
      input({ onkeyup: link(elem), value: elem.value });
      button({ type: 'submit' }, 'add');
    });
    slot(function () {
      p('There is nothing to do.');
    });
  }
});

const Xitem = kickflip('x-item', {
  events: {
    'click button' () {
      emit(this, 'todo-remove', { detail: this.textContent });
    }
  },
  render () {
    slot();
    text(' ');
    button('remove');
  }
});
<|endoftext|>