<commit_before>/* eslint-disable react/prefer-stateless-function */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withRouter, Link, NavLink } from 'react-router-dom';
import { addFlashMessage } from '../../../actions/messageActions';
import { logout } from '../../../actions/authActions';

class Header extends Component {
  constructor(props) {
    super(props);
    this.handleLogout = this.handleLogout.bind(this);
  }
  handleLogout(e) {
    e.preventDefault();
    logout(this.state);
    this.props.addFlashMessage({
      type: 'success',
      text: 'Successfully Logged out.',
    });
    return this.props.history.push('/');
  }
  render() {
    return (

            <Link to="/shelf">
Hellobooks
</Link>

            <Link to="/" className="brand-logo">HelloBooks</Link>
            <Link to="#" data-activates="mobile-demo" className="button-collapse">menu</Link>

<NavLink activeClassName="active" to="/shelf">Shelf</NavLink>

<NavLink activeClassName="active" to="/history">History</NavLink>

<NavLink activeClassName="active" to="/admin"> Admin Dashboard</NavLink>

<NavLink activeClassName="active" to="/profile"> Profile</NavLink>

<Link onClick={this.handleLogout} to=""> Logout</Link>

<Link to="">HelloBooks</Link>

<NavLink activeClassName="active" to="/shelf">Shelf</NavLink>

<NavLink activeClassName="active" to="/history">History</NavLink>

<NavLink activeClassName="active" to="/admin"> Admin Dashboard</NavLink>

<NavLink activeClassName="active" to="/profile"> Profile</NavLink>

<Link onClick={this.handleLogout} to="/"> Logout</Link>

    );
  }
}

Header.propTypes = {
  history: PropTypes.object.isRequired,
  addFlashMessage: PropTypes.func.isRequired,
};

export default connect(null, { logout, addFlashMessage })(withRouter(Header));
<commit_msg>Fix navigation menu active class bug<commit_after>/* eslint-disable react/prefer-stateless-function */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withRouter, Link, NavLink } from 'react-router-dom';
import { addFlashMessage } from '../../../actions/messageActions';
import { logout } from '../../../actions/authActions';

class Header extends Component {
  constructor(props) {
    super(props);
    this.handleLogout = this.handleLogout.bind(this);
  }
  handleLogout(e) {
    e.preventDefault();
    logout(this.state);
    this.props.addFlashMessage({
      type: 'success',
      text: 'Successfully Logged out.',
    });
    return this.props.history.push('/');
  }
  render() {
    return (

            <Link to="/shelf">
Hellobooks
</Link>

            <Link to="/" className="brand-logo">HelloBooks</Link>
            <Link to="#" data-activates="mobile-demo" className="button-collapse">menu</Link>

<NavLink activeClassName="active" to="/shelf">Shelf</NavLink>

<NavLink activeClassName="active" to="/history">History</NavLink>

<NavLink activeClassName="active" to="/admin"> Admin Dashboard</NavLink>

<NavLink activeClassName="active" to="/profile"> Profile</NavLink>

<Link onClick={this.handleLogout} to=""> Logout</Link>

<Link to="">HelloBooks</Link>

<NavLink activeClassName="active" to="/shelf">Shelf</NavLink>

<NavLink activeClassName="active" to="/history">History</NavLink>

<NavLink activeClassName="active" to="/admin"> Admin Dashboard</NavLink>

<NavLink activeClassName="active" to="/profile"> Profile</NavLink>

<Link onClick={this.handleLogout} to="/"> Logout</Link>

    );
  }
}

Header.propTypes = {
  history: PropTypes.object.isRequired,
  addFlashMessage: PropTypes.func.isRequired,
};

export default withRouter(connect(null, { logout, addFlashMessage })(Header));
<|endoftext|>


<commit_before>var repl = require("repl");

// 스타트 메서드 호출.
// 각 파라미터 기본값 : >, process.stdin, eval, false, false
// repl.start([prompt], [stream], [eval], [useGlobal], [ignoreUndefined]);

// null 로 설정하면 기본값으로 설정 됨.
repl.start("node via stdin> ", null, null, null, true);

<commit_msg>update customREPL.js   - load module in advance<commit_after>var repl = require("repl");

// 스타트 메서드 호출.
// 각 파라미터 기본값 : >, process.stdin, eval, false, false
// repl.start([prompt], [stream], [eval], [useGlobal], [ignoreUndefined]);

// null 로 설정하면 기본값으로 설정 됨.
var context = repl.start(">>", null, null, null, true).context;

// 모듈들을 사전에 로드
context.http = require('http');
context.util = require('util');
context.os = require('os');

<|endoftext|>


<commit_before>var path = require('path');
var webpack = require('webpack');

var config = {
	entry: './src/select',
	externals: {
        'react': 'React',
        'react-dom': 'ReactDOM'
    },
	output: {
		path: path.join(__dirname, '../build'),
		filename: 'react-select.js'
	},
	plugins: [
		new webpack.DefinePlugin({
			"process.env": {
				NODE_ENV: JSON.stringify("production")
			}
		}),
		new webpack.optimize.UglifyJsPlugin({
			compress: {
				warnings: false
			}
		})
	],
	module: {
		loaders: [{
			test: /\.js$/,
			loader: 'babel',
			query: {
				presets: ['es2015', 'react']
			},
			include: path.join(__dirname, '../src'),
			exclude: ['node_modules']
		},{
			test: /\.less$/, loader: "style-loader!css-loader!less-loader",
			include: path.join(__dirname, '../src'),
			exclude: ['node_modules']
		}]
	}
};

webpack(config, function(err, stats) {
	if(err){
		console.log(err);
	} else {
		console.log('JS build success!');
	}
});

var fs = require('fs');
var less = require('less');

less.render('@import "default.less";', {
	paths: [path.join(__dirname, '../src/less')],  // Specify search paths for @import directives
	filename: 'style.less', // Specify a filename, for better error messages
	compress: true          // Minify CSS output
}, function (e, output) {
	if(e){
		console.log(e.message);
	} else {
		fs.writeFile(path.join(__dirname, '../build/style.css'), output.css, {flag: 'w+'}, function (err) {
			if (err) throw err;
			console.log('CSS build success!');
		});
	}
});<commit_msg>update build script<commit_after>var path = require('path');
var webpack = require('webpack');

var config = {
	entry: './src/select',
	externals: {
        'react': 'React',
        'react-dom': 'ReactDOM'
    },
	output: {
		path: path.join(__dirname, '../build'),
		filename: 'react-select.js'
	},
	plugins: [
		new webpack.DefinePlugin({
			"process.env": {
				NODE_ENV: JSON.stringify("production")
			}
		}),
		new webpack.optimize.UglifyJsPlugin({
			compress: {
				warnings: false
			}
		})
	],
	module: {
		loaders: [{
			test: /\.js$/,
			loader: 'babel',
			query: {
				presets: ['es2015', 'react']
			},
			include: path.join(__dirname, '../src'),
			exclude: ['node_modules']
		},{
			test: /\.less$/, loader: "style-loader!css-loader!less-loader",
			include: path.join(__dirname, '../src'),
			exclude: ['node_modules']
		}]
	}
};

webpack(config, function(err, stats) {
	if(err){
		console.log(err);
	} else {
		console.log('JS build success!');
	}
});

var fs = require('fs');
var less = require('less');

less.render('@import "default.less";', {
	paths: [path.join(__dirname, '../src/less')],  // Specify search paths for @import directives
	filename: 'style.less', // Specify a filename, for better error messages
	compress: true          // Minify CSS output
}, function (e, output) {
	if(e){
		console.log(e.message);
	} else {
		var buildPath = path.join(__dirname, '../build');
		if(!fs.existsSync(buildPath)){
			fs.mkdirSync(buildPath);
		} else {
			var info = fs.statSync(buildPath);
			if(!info.isDirectory()){
				fs.unlinkSync(buildPath);
				fs.mkdirSync(buildPath);
			}
		}
		fs.writeFile(path.join(__dirname, '../build/style.css'), output.css, {flag: 'w+'}, function (err) {
			if (err) throw err;
			console.log('CSS build success!');
		});
	}
});<|endoftext|>


<commit_before>'use strict';

const pg       = require('pg').native;
const request  = require('request');
const cheerio  = require('cheerio');
const async    = require('async');
const moment   = require('moment');
const _        = require('underscore');
const argv     = require('yargs').argv;

const CATALOG_URL = 'http://catalog.oregonstate.edu/';
const COURSE_SEARCH_URL = CATALOG_URL + 'CourseSearcher.aspx?chr=abg';
const SAVE = !argv.nosave;

if (argv.help) {
  console.log('OSU Course Catalog scraper. Supply a Postgres database to ' +
  'into with -db or supply a DB_URL environment var. Skip saving with ' +
  '--nosave option.');
  process.exit();
}

/////////////////////////////////////////////////
// DATABASE
/////////////////////////////////////////////////

const PG_URL = argv.db || process.env.DATABASE_URL;
const client = new pg.Client(PG_URL);

if (SAVE) {
  client.connect(function(err) {
    if (err) {
      console.error('could not connect to postgres', err);
      process.exit();
    } else {
      console.log('Connected to PostgreSQL');
      getCourseLinks(function(courses, err) {
        console.log('Scrape complete.');
        client.on('drain', client.end.bind(client));
      });
    }
  });

  client.on('error', function(err) {
    throw(err);
  });

} else {
  getCourseLinks(function(courses, err) {
    console.log('Scrape complete.');
  });
}

function insertCourseAndSections(courseObject, sectionObjects) {
  var courseInsertion = insertCourse(courseObject, function(key) {
    insertSections(sectionObjects, key);
  });
}

function insertCourse(courseObject, callback) {
  const course = courseObject;
  var query = client.query({
    text: 'INSERT INTO course (' +
      'abbr,' +
      'title,' +
      'credits,' +
      'description' +
    ') VALUES ($1, $2, $3, $4)' +
    'RETURNING id',
    values: [
      course.abbr,
      course.title,
      course.credits,
      course.description,
    ],
    name: 'insert course',
  });

  query.on('row', function(row) {
    const id = row.id;
    callback(id);
  });

  query.on('error', function(err) {
    console.error(err);
  });
}

function insertSections(sectionObjectArray, courseId) {
  _.each(sectionObjectArray, function(sectionObject) {
    insertSection(sectionObject, courseId);
  });
}

function insertSection(sectionObject, courseId) {
  const section = sectionObject;

  var query = client.query({
    text: 'INSERT INTO section (' +
      'crn,' +
      'course_id,' +
      'term,' +
      'start_date,' +
      'end_date,' +
      'section,' +
      'session,' +
      'credits,' +
      'instructor,' +
      'days,' +
      'start_time,' +
      'end_time,' +
      'location,' +
      'campus,' +
      'type,' +
      'status,' +
      'capacity,' +
      'enrolled,' +
      'waitlist_cap,' +
      'waitlist_current,' +
      'fees,' +
      'restrictions,' +
      'comments' +
    ') VALUES (' +
      '$1 , $2 , $3 , $4 , $5 , $6 , $7 , $8 , $9 , $10,' +
      '$11, $12, $13, $14, $15, $16, $17, $18, $19, $20,' +
      '$21, $22, $23' +
    ')',
    values: [
      section.crn,
      courseId,
      section.term,
      section.startDate,
      section.endDate,
      section.sec,
      section.session,
      [section.cr], // TODO: Make this include the correct range
      section.instructor,
      section.days,
      section.startTime,
      section.endTime,
      section.location,
      section.campus,
      section.type,
      section.status,
      section.cap,
      section.curr,
      section.wlcurr,
      section.wlcap,
      section.fees,
      section.restrictions,
      section.comments,
    ],
    name: 'insert section',
  });

  query.on('error', function(err) {
    console.error(err);
  });
}

/////////////////////////////////////////////////
// MAIN
/////////////////////////////////////////////////

// Scrapes the search page for links to all course pages
function getCourseLinks(callback) {
  request(COURSE_SEARCH_URL, function parseSearchPage(error, res, body) {
    if (!error && res.statusCode === 200) {
      var classURLs = [];
      var $ = cheerio.load(body);

      $('a[id^=\'ctl00_ContentPlaceHolder\']').each(function(i, element) {
        var link = $(this).attr('href');
        classURLs.push(link);
      });

      // First two elements are currently trash, don't attempt to scrape
      classURLs.splice(0, 2);
      return getCourseInfo(CATALOG_URL, classURLs, callback);
    }
  });
}

// Loads each course page into cheerio and calls parseCourse on it
function getCourseInfo(baseURL, classURLs, callback) {
  var index = 1;

  async.eachSeries(classURLs, function(url, asyncCallback) {
    const classURL = baseURL + url;
    console.log('Scraping ' + index++ + ' of ' + classURLs.length);
    console.log('URL: ' + classURL);

    request(classURL, function(error, response, body) {
      if (error) {
        console.error('Error scraping ' + classURL + '\n' + error);
      } else if (response.statusCode !== 200) {
        console.error('Reponse status code == ' + response.statusCode);
      } else {
        const $ = cheerio.load(body);

        const courseObject = parseCourse($);
        var sectionObjects = parseSections($);
        if (SAVE) {
          insertCourseAndSections(courseObject, sectionObjects);
        }
      }

      asyncCallback();
    });
  },

  function doneScraping(err) {
    if (err) {
      console.error('An error occured scraping: ' + err);
    } else {
      callback();
    }
  });
}

/////////////////////////////////////////////////
// PARSERS
/////////////////////////////////////////////////

function parseCourse($) {
  return {
    abbr: parseAbbr($),
    title: parseTitle($),
    credits: parseCredits($),
    description: parseDesc($),
  };
}

// Gets course title from the class site. Regex's follow these steps:
// Select h3, remove abbreviation, remove credits, replace
// non-words/whitespace with whitespace, remove spaces, tabs &
// newlines, turn multiple spaces into one, remove spaces on ends
function parseTitle($) {
  var title = $('h3').text();
  title = title.replace(/(^[A-Z]{1,4}\s[0-9]{2,3})/, '');
  title = title.replace(/\(([^\)]+)\)/i, '');
  title = title.replace(/[^\w\s]/gi, ' ');
  title = title.replace(/\r?\n|\r|\t/g, '');
  title = title.replace(/\ {2,}/g, ' ');
  return title.trim();
}

// Gets course abbreviation from the class site
function parseAbbr($) {
  return $('h3').text().trim().match(/^[A-Z]{1,4}\s[0-9]{2,3}/)[0];
}

// Gets credits from the class site
function parseCredits($) {
  return $('h3')
  .text()
  .match(/\(([^\)]+)\)/i)[1]
  .split('-');
}

// Gets course description from the class site
function parseDesc($) {
  var desc = $('#aspnetForm').first()
                             .clone()
                             .children()
                             .remove()
                             .end()
                             .text()
                             .trim();
  desc = trimNewlines(desc);
  return desc;
}

// Parses table of class sections
function parseSections($) {
  var columnNames = [];
  $('th').each(function(i, element) {
    var columnName = $(this).text().replace(/\r?\n|\r|\t/g, '');
    columnName = columnName.replace(/\ {2,}/g, '');
    columnNames.push(columnName);
  });

  var tableRows = $('#ctl00_ContentPlaceHolder1_SOCListUC1_gvOfferings > tr ');
  var sections = [];

  tableRows.each(function parseSection(i, element) {
    var sectionDict = {};
    const td = $(this).children('td');

    td.each(function(i) {
      // trim is likely not necessary
      const data = $(this).text().replace(/\s{2,}/g, ' ').trim();
      const key = formatKey(columnNames[i % columnNames.length]);

      // Parse date
      if (key === 'daytimedate') {
        const text = $(this).text();
        parseSectionDate(text, sectionDict);
      } else {
        sectionDict[key] = data;
      }
    });

    sections.push(sectionDict);
  });

  // First element will be categories, so remove it
  sections.splice(0, 1);
  return sections;
}

// Returns object w/ keys for days, startTime, endTime, startDate & endDate
function parseSectionDate(text, sectionDict) {
  if (text.match(/\d+/g) && text.indexOf('TBA') === -1) {
    sectionDict.days = text
    .match(/([A-Z])+/g)[0].split('');

    sectionDict.startTime = moment(text
    .match(/[0-9]{4}/g)[0], 'HHmm')
    .format('HH:mm:ss');

    sectionDict.endTime = moment(text
    .match(/[0-9]{4}/g)[1], 'HHmm')
    .format('HH:mm:ss');

    sectionDict.startDate = text
    .match(/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,2}/g)[0];

    sectionDict.endDate = text
    .match(/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,2}/g)[1];
  }
}

/////////////////////////////////////////////////
// HELPER FUNCTIONS
/////////////////////////////////////////////////

function trimNewlines(desc) {
  var n = desc.indexOf('\n');
  n = n === -1 ? desc.length : n;
  return desc.substring(0, n);
}

function formatKey(key) {
  return key.toLowerCase().replace(/[^A-z]/g, '');
}
<commit_msg>Now parses credits correctly for courses.<commit_after>'use strict';

const pg       = require('pg').native;
const request  = require('request');
const cheerio  = require('cheerio');
const async    = require('async');
const moment   = require('moment');
const _        = require('underscore');
const argv     = require('yargs').argv;

const CATALOG_URL = 'http://catalog.oregonstate.edu/';
const COURSE_SEARCH_URL = CATALOG_URL + 'CourseSearcher.aspx?chr=abg';
const SAVE = !argv.nosave;

if (argv.help) {
  console.log('OSU Course Catalog scraper. Supply a Postgres database to ' +
  'into with -db or supply a DB_URL environment var. Skip saving with ' +
  '--nosave option.');
  process.exit();
}

/////////////////////////////////////////////////
// DATABASE
/////////////////////////////////////////////////

const PG_URL = argv.db || process.env.DATABASE_URL;
const client = new pg.Client(PG_URL);

if (SAVE) {
  client.connect(function(err) {
    if (err) {
      console.error('could not connect to postgres', err);
      process.exit();
    } else {
      console.log('Connected to PostgreSQL');
      getCourseLinks(function(courses, err) {
        console.log('Scrape complete.');
        client.on('drain', client.end.bind(client));
      });
    }
  });

  client.on('error', function(err) {
    throw(err);
  });

} else {
  getCourseLinks(function(courses, err) {
    console.log('Scrape complete.');
  });
}

function insertCourseAndSections(courseObject, sectionObjects) {
  var courseInsertion = insertCourse(courseObject, function(key) {
    insertSections(sectionObjects, key);
  });
}

function insertCourse(courseObject, callback) {
  const course = courseObject;
  var query = client.query({
    text: 'INSERT INTO course (' +
      'abbr,' +
      'title,' +
      'credits,' +
      'description' +
    ') VALUES ($1, $2, $3, $4)' +
    'RETURNING id',
    values: [
      course.abbr,
      course.title,
      course.credits,
      course.description,
    ],
    name: 'insert course',
  });

  query.on('row', function(row) {
    const id = row.id;
    callback(id);
  });

  query.on('error', function(err) {
    console.error(err);
  });
}

function insertSections(sectionObjectArray, courseId) {
  _.each(sectionObjectArray, function(sectionObject) {
    insertSection(sectionObject, courseId);
  });
}

function insertSection(sectionObject, courseId) {
  const section = sectionObject;

  var query = client.query({
    text: 'INSERT INTO section (' +
      'crn,' +
      'course_id,' +
      'term,' +
      'start_date,' +
      'end_date,' +
      'section,' +
      'session,' +
      'credits,' +
      'instructor,' +
      'days,' +
      'start_time,' +
      'end_time,' +
      'location,' +
      'campus,' +
      'type,' +
      'status,' +
      'capacity,' +
      'enrolled,' +
      'waitlist_cap,' +
      'waitlist_current,' +
      'fees,' +
      'restrictions,' +
      'comments' +
    ') VALUES (' +
      '$1 , $2 , $3 , $4 , $5 , $6 , $7 , $8 , $9 , $10,' +
      '$11, $12, $13, $14, $15, $16, $17, $18, $19, $20,' +
      '$21, $22, $23' +
    ')',
    values: [
      section.crn,
      courseId,
      section.term,
      section.startDate,
      section.endDate,
      section.sec,
      section.session,
      section.cr,
      section.instructor,
      section.days,
      section.startTime,
      section.endTime,
      section.location,
      section.campus,
      section.type,
      section.status,
      section.cap,
      section.curr,
      section.wlcurr,
      section.wlcap,
      section.fees,
      section.restrictions,
      section.comments,
    ],
    name: 'insert section',
  });

  query.on('error', function(err) {
    console.error(err);
  });
}

/////////////////////////////////////////////////
// MAIN
/////////////////////////////////////////////////

// Scrapes the search page for links to all course pages
function getCourseLinks(callback) {
  request(COURSE_SEARCH_URL, function parseSearchPage(error, res, body) {
    if (!error && res.statusCode === 200) {
      var classURLs = [];
      var $ = cheerio.load(body);

      $('a[id^=\'ctl00_ContentPlaceHolder\']').each(function(i, element) {
        var link = $(this).attr('href');
        classURLs.push(link);
      });

      // First two elements are currently trash, don't attempt to scrape
      classURLs.splice(0, 2);
      return getCourseInfo(CATALOG_URL, classURLs, callback);
    }
  });
}

// Loads each course page into cheerio and calls parseCourse on it
function getCourseInfo(baseURL, classURLs, callback) {
  var index = 1;

  async.eachSeries(classURLs, function(url, asyncCallback) {
    const classURL = baseURL + url;
    console.log('Scraping ' + index++ + ' of ' + classURLs.length);
    console.log('URL: ' + classURL);

    request(classURL, function(error, response, body) {
      if (error) {
        console.error('Error scraping ' + classURL + '\n' + error);
      } else if (response.statusCode !== 200) {
        console.error('Reponse status code == ' + response.statusCode);
      } else {
        const $ = cheerio.load(body);

        const courseObject = parseCourse($);
        var sectionObjects = parseSections($);
        if (SAVE) {
          insertCourseAndSections(courseObject, sectionObjects);
        }
      }

      asyncCallback();
    });
  },

  function doneScraping(err) {
    if (err) {
      console.error('An error occured scraping: ' + err);
    } else {
      callback();
    }
  });
}

/////////////////////////////////////////////////
// PARSERS
/////////////////////////////////////////////////

function parseCourse($) {
  return {
    abbr: parseAbbr($),
    title: parseTitle($),
    credits: parseCredits($),
    description: parseDesc($),
  };
}

// Gets course title from the class site. Regex's follow these steps:
// Select h3, remove abbreviation, remove credits, replace
// non-words/whitespace with whitespace, remove spaces, tabs &
// newlines, turn multiple spaces into one, remove spaces on ends
function parseTitle($) {
  var title = $('h3').text();
  title = title.replace(/(^[A-Z]{1,4}\s[0-9]{2,3})/, '');
  title = title.replace(/\(([^\)]+)\)/i, '');
  title = title.replace(/[^\w\s]/gi, ' ');
  title = title.replace(/\r?\n|\r|\t/g, '');
  title = title.replace(/\ {2,}/g, ' ');
  return title.trim();
}

// Gets course abbreviation from the class site
function parseAbbr($) {
  return $('h3').text().trim().match(/^[A-Z]{1,4}\s[0-9]{2,3}/)[0];
}

// Gets credits from the class site and parses string array to integer array
function parseCredits($) {
  return $('h3')
  .text()
  .match(/\(([^\)]+)\)/i)[1]
  .split('-')
  .map(function(value) {
    return parseInt(value, 10);
  });
}

// Gets course description from the class site
function parseDesc($) {
  var desc = $('#aspnetForm').first()
                             .clone()
                             .children()
                             .remove()
                             .end()
                             .text()
                             .trim();
  desc = trimNewlines(desc);
  return desc;
}

// Parses table of class sections
function parseSections($) {
  var columnNames = [];
  $('th').each(function(i, element) {
    var columnName = $(this).text().replace(/\r?\n|\r|\t/g, '');
    columnName = columnName.replace(/\ {2,}/g, '');
    columnNames.push(columnName);
  });

  var tableRows = $('#ctl00_ContentPlaceHolder1_SOCListUC1_gvOfferings > tr ');
  var sections = [];

  tableRows.each(function parseSection(i, element) {
    var sectionDict = {};
    const td = $(this).children('td');

    td.each(function(i) {
      // trim is likely not necessary
      const data = $(this).text().replace(/\s{2,}/g, ' ').trim();
      const key = formatKey(columnNames[i % columnNames.length]);

      // Parse date
      if (key === 'daytimedate') {
        const text = $(this).text();
        parseSectionDate(text, sectionDict);
      } else {
        sectionDict[key] = data;
      }
    });

    sections.push(sectionDict);
  });

  // First element will be categories, so remove it
  sections.splice(0, 1);
  return sections;
}

// Returns object w/ keys for days, startTime, endTime, startDate & endDate
function parseSectionDate(text, sectionDict) {
  if (text.match(/\d+/g) && text.indexOf('TBA') === -1) {
    sectionDict.days = text
    .match(/([A-Z])+/g)[0].split('');

    sectionDict.startTime = moment(text
    .match(/[0-9]{4}/g)[0], 'HHmm')
    .format('HH:mm:ss');

    sectionDict.endTime = moment(text
    .match(/[0-9]{4}/g)[1], 'HHmm')
    .format('HH:mm:ss');

    sectionDict.startDate = text
    .match(/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,2}/g)[0];

    sectionDict.endDate = text
    .match(/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,2}/g)[1];
  }
}

/////////////////////////////////////////////////
// HELPER FUNCTIONS
/////////////////////////////////////////////////

function trimNewlines(desc) {
  var n = desc.indexOf('\n');
  n = n === -1 ? desc.length : n;
  return desc.substring(0, n);
}

function formatKey(key) {
  return key.toLowerCase().replace(/[^A-z]/g, '');
}
<|endoftext|>


<commit_before> /**
 *  Copyright (C) 2010 Cloud.com, Inc.  All rights reserved.
 *
 * This software is licensed under the GNU General Public License v3 or later.
 *
 * It is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

var systemAccountId = 1;
var adminAccountId = 2;

function afterLoadAccountJSP() {
    initDialog("dialog_resource_limits");
    initDialog("dialog_disable_account");
    initDialog("dialog_lock_account");
    initDialog("dialog_enable_account");
}

function accountToMidmenu(jsonObj, $midmenuItem1) {
    $midmenuItem1.attr("id", getMidmenuId(jsonObj));
    $midmenuItem1.data("jsonObj", jsonObj);

    var $iconContainer = $midmenuItem1.find("#icon_container").show();
    if (jsonObj.accounttype == roleTypeUser)
        $iconContainer.find("#icon").attr("src", "images/midmenuicon_account_user.png");
	else if (jsonObj.accounttype == roleTypeAdmin)
	    $iconContainer.find("#icon").attr("src", "images/midmenuicon_account_admin.png");
	else if (jsonObj.accounttype == roleTypeDomainAdmin)
	    $iconContainer.find("#icon").attr("src", "images/midmenuicon_account_domain.png");

    $midmenuItem1.find("#first_row").text(fromdb(jsonObj.name).substring(0,25));
    $midmenuItem1.find("#second_row").text(fromdb(jsonObj.domain).substring(0,25));
}

function accountToRightPanel($midmenuItem1) {
    copyActionInfoFromMidMenuToRightPanel($midmenuItem1);
    accountJsonToDetailsTab($midmenuItem1);
}

function accountJsonToDetailsTab($midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");
    var $detailsTab = $("#right_panel_content #tab_content_details");
    $detailsTab.data("jsonObj", jsonObj);

    $detailsTab.find("#grid_header_title").text(fromdb(jsonObj.name));
    $detailsTab.find("#id").text(noNull(jsonObj.id));
    $detailsTab.find("#role").text(toRole(jsonObj.accounttype));
    $detailsTab.find("#account").text(fromdb(jsonObj.name));
    $detailsTab.find("#domain").text(fromdb(jsonObj.domain));
    $detailsTab.find("#vm_total").text(noNull(jsonObj.vmtotal));
    $detailsTab.find("#ip_total").text(noNull(jsonObj.iptotal));
    $detailsTab.find("#bytes_received").text(convertBytes(jsonObj.receivedbytes));
    $detailsTab.find("#bytes_sent").text(convertBytes(jsonObj.sentbytes));
    $detailsTab.find("#state").text(noNull(jsonObj.state));

    //actions ***
    var $actionMenu = $("#right_panel_content #tab_content_details #action_link #action_menu");
    $actionMenu.find("#action_list").empty();
    var noAvailableActions = true;

    if(jsonObj.id != systemAccountId && jsonObj.id != adminAccountId) {
        if (jsonObj.accounttype == roleTypeUser || jsonObj.accounttype == roleTypeDomainAdmin) {
            buildActionLinkForTab("Resource limits", accountActionMap, $actionMenu, $midmenuItem1, $detailsTab);
            noAvailableActions = false;
        }

        if(jsonObj.state == "enabled") {
            buildActionLinkForTab("Disable account", accountActionMap, $actionMenu, $midmenuItem1, $detailsTab);
            buildActionLinkForTab("Lock account", accountActionMap, $actionMenu, $midmenuItem1, $detailsTab);
            noAvailableActions = false;
        }
        else if(jsonObj.state == "disabled" || jsonObj.state == "locked") {
            buildActionLinkForTab("Enable account", accountActionMap, $actionMenu, $midmenuItem1, $detailsTab);
            noAvailableActions = false;
        }
    }

    // no available actions
	if(noAvailableActions == true) {
	    $actionMenu.find("#action_list").append($("#no_available_actions").clone().show());
	}
}

var accountActionMap = {
    "Resource limits": {
        dialogBeforeActionFn : doResourceLimitsForAccount
    }
    ,
    "Disable account": {
        isAsyncJob: true,
        asyncJobResponse: "disableaccountresponse",
        dialogBeforeActionFn : doDisableAccount,
        inProcessText: "Disabling account....",
        afterActionSeccessFn: function(json, $midmenuItem1, id) {
            var item = json.queryasyncjobresultresponse.jobresult.account;
            accountToMidmenu(item, $midmenuItem1);
            accountJsonToDetailsTab($midmenuItem1);
        }
    }
    ,
    "Lock account": {
        isAsyncJob: false,
        dialogBeforeActionFn : doLockAccount,
        inProcessText: "Locking account....",
        afterActionSeccessFn: function(json, $midmenuItem1, id) {
            var item = json.lockaccountresponse.account;
            accountToMidmenu(item, $midmenuItem1);
            accountJsonToDetailsTab($midmenuItem1);
        }
    }
    ,
    "Enable account": {
        isAsyncJob: false,
        dialogBeforeActionFn : doEnableAccount,
        inProcessText: "Enabling account....",
        afterActionSeccessFn: function(json, $midmenuItem1, id) {
            var item = json.enableaccountresponse.account;
            accountToMidmenu(item, $midmenuItem1);
            accountJsonToDetailsTab($midmenuItem1);
        }
    }
};

function updateResourceLimitForAccount(domainId, account, type, max) {
	$.ajax({
	    data: createURL("command=updateResourceLimit&domainid="+domainId+"&account="+account+"&resourceType="+type+"&max="+max),
		dataType: "json",
		success: function(json) {
		}
	});
}

function doResourceLimitsForAccount($actionLink, $detailsTab, $midmenuItem1) {
    var $detailsTab = $("#right_panel_content #tab_content_details");
	var jsonObj = $detailsTab.data("jsonObj");
	var domainId = jsonObj.domainid;
	var account = jsonObj.name;
	$.ajax({
		cache: false,
		data: createURL("command=listResourceLimits&domainid="+domainId+"&account="+account),
		dataType: "json",
		success: function(json) {
			var limits = json.listresourcelimitsresponse.resourcelimit;
			var preInstanceLimit, preIpLimit, preDiskLimit, preSnapshotLimit, preTemplateLimit = -1;
			if (limits != null) {
				for (var i = 0; i < limits.length; i++) {
					var limit = limits[i];
					switch (limit.resourcetype) {
						case "0":
							preInstanceLimit = limit.max;
							$("#dialog_resource_limits #limits_vm").val(limit.max);
							break;
						case "1":
							preIpLimit = limit.max;
							$("#dialog_resource_limits #limits_ip").val(limit.max);
							break;
						case "2":
							preDiskLimit = limit.max;
							$("#dialog_resource_limits #limits_volume").val(limit.max);
							break;
						case "3":
							preSnapshotLimit = limit.max;
							$("#dialog_resource_limits #limits_snapshot").val(limit.max);
							break;
						case "4":
							preTemplateLimit = limit.max;
							$("#dialog_resource_limits #limits_template").val(limit.max);
							break;
					}
				}
			}
			$("#dialog_resource_limits")
			.dialog('option', 'buttons', {
				"Save": function() {
					// validate values
					var isValid = true;
					isValid &= validateNumber("Instance Limit", $("#dialog_resource_limits #limits_vm"), $("#dialog_resource_limits #limits_vm_errormsg"), -1, 32000, false);
					isValid &= validateNumber("Public IP Limit", $("#dialog_resource_limits #limits_ip"), $("#dialog_resource_limits #limits_ip_errormsg"), -1, 32000, false);
					isValid &= validateNumber("Disk Volume Limit", $("#dialog_resource_limits #limits_volume"), $("#dialog_resource_limits #limits_volume_errormsg"), -1, 32000, false);
					isValid &= validateNumber("Snapshot Limit", $("#dialog_resource_limits #limits_snapshot"), $("#dialog_resource_limits #limits_snapshot_errormsg"), -1, 32000, false);
					isValid &= validateNumber("Template Limit", $("#dialog_resource_limits #limits_template"), $("#dialog_resource_limits #limits_template_errormsg"), -1, 32000, false);
					if (!isValid) return;

					var instanceLimit = trim($("#dialog_resource_limits #limits_vm").val());
					var ipLimit = trim($("#dialog_resource_limits #limits_ip").val());
					var diskLimit = trim($("#dialog_resource_limits #limits_volume").val());
					var snapshotLimit = trim($("#dialog_resource_limits #limits_snapshot").val());
					var templateLimit = trim($("#dialog_resource_limits #limits_template").val());

					$(this).dialog("close");
					if (instanceLimit != preInstanceLimit) {
						updateResourceLimitForAccount(domainId, account, 0, instanceLimit);
					}
					if (ipLimit != preIpLimit) {
						updateResourceLimitForAccount(domainId, account, 1, ipLimit);
					}
					if (diskLimit != preDiskLimit) {
						updateResourceLimitForAccount(domainId, account, 2, diskLimit);
					}
					if (snapshotLimit != preSnapshotLimit) {
						updateResourceLimitForAccount(domainId, account, 3, snapshotLimit);
					}
					if (templateLimit != preTemplateLimit) {
						updateResourceLimitForAccount(domainId, account, 4, templateLimit);
					}
				},
				"Cancel": function() {
					$(this).dialog("close");
				}
			}).dialog("open");
		}
	});
}

function doDisableAccount($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $detailsTab.data("jsonObj");
    var id = jsonObj.id;

    $("#dialog_disable_account")
    .dialog('option', 'buttons', {
        "Yes": function() {
            $(this).dialog("close");
			var apiCommand = "command=disableAccount&account="+jsonObj.name+"&domainId="+jsonObj.domainid;
	    	doActionToTab(id, $actionLink, apiCommand, $midmenuItem1, $detailsTab) ;
        },
        "Cancel": function() {
            $(this).dialog("close");
        }
    }).dialog("open");
}

function doLockAccount($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $detailsTab.data("jsonObj");

    $("#dialog_lock_account")
    .dialog('option', 'buttons', {
        "Yes": function() {
            $(this).dialog("close");
			var apiCommand = "command=lockAccount&account="+jsonObj.name+"&domainId="+jsonObj.domainid;
	    	doActionToTab(jsonObj.id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
        },
        "Cancel": function() {
            $(this).dialog("close");
        }
    }).dialog("open");
}

function doEnableAccount($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $detailsTab.data("jsonObj");

    $("#dialog_enable_account")
    .dialog('option', 'buttons', {
        "Yes": function() {
            $(this).dialog("close");
			var apiCommand = "command=enableAccount&account="+jsonObj.name+"&domainId="+jsonObj.domainid;
	    	doActionToTab(jsonObj.id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
        },
        "Cancel": function() {
            $(this).dialog("close");
        }
    }).dialog("open");
}<commit_msg>new UI - Account page - fix a bug that JS error "$midmenuItem1 is null or not an object ", "jsonObj is null or not an object" when switching between tabs on an empty page.<commit_after> /**
 *  Copyright (C) 2010 Cloud.com, Inc.  All rights reserved.
 *
 * This software is licensed under the GNU General Public License v3 or later.
 *
 * It is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

var systemAccountId = 1;
var adminAccountId = 2;

function afterLoadAccountJSP() {
    initDialog("dialog_resource_limits");
    initDialog("dialog_disable_account");
    initDialog("dialog_lock_account");
    initDialog("dialog_enable_account");
}

function accountToMidmenu(jsonObj, $midmenuItem1) {
    $midmenuItem1.attr("id", getMidmenuId(jsonObj));
    $midmenuItem1.data("jsonObj", jsonObj);

    var $iconContainer = $midmenuItem1.find("#icon_container").show();
    if (jsonObj.accounttype == roleTypeUser)
        $iconContainer.find("#icon").attr("src", "images/midmenuicon_account_user.png");
	else if (jsonObj.accounttype == roleTypeAdmin)
	    $iconContainer.find("#icon").attr("src", "images/midmenuicon_account_admin.png");
	else if (jsonObj.accounttype == roleTypeDomainAdmin)
	    $iconContainer.find("#icon").attr("src", "images/midmenuicon_account_domain.png");

    $midmenuItem1.find("#first_row").text(fromdb(jsonObj.name).substring(0,25));
    $midmenuItem1.find("#second_row").text(fromdb(jsonObj.domain).substring(0,25));
}

function accountToRightPanel($midmenuItem1) {
    copyActionInfoFromMidMenuToRightPanel($midmenuItem1);
    $("#right_panel_content").data("$midmenuItem1", $midmenuItem1);
    accountJsonToDetailsTab();
}

function accountJsonToDetailsTab() {
    var $midmenuItem1 = $("#right_panel_content").data("$midmenuItem1");
    if($midmenuItem1 == null)
        return;

    var jsonObj = $midmenuItem1.data("jsonObj");
    if(jsonObj == null)
        return;

    var $detailsTab = $("#right_panel_content").find("#tab_content_details");

    $detailsTab.find("#grid_header_title").text(fromdb(jsonObj.name));
    $detailsTab.find("#id").text(noNull(jsonObj.id));
    $detailsTab.find("#role").text(toRole(jsonObj.accounttype));
    $detailsTab.find("#account").text(fromdb(jsonObj.name));
    $detailsTab.find("#domain").text(fromdb(jsonObj.domain));
    $detailsTab.find("#vm_total").text(noNull(jsonObj.vmtotal));
    $detailsTab.find("#ip_total").text(noNull(jsonObj.iptotal));
    $detailsTab.find("#bytes_received").text(convertBytes(jsonObj.receivedbytes));
    $detailsTab.find("#bytes_sent").text(convertBytes(jsonObj.sentbytes));
    $detailsTab.find("#state").text(noNull(jsonObj.state));

    //actions ***
    var $actionMenu = $("#right_panel_content #tab_content_details #action_link #action_menu");
    $actionMenu.find("#action_list").empty();
    var noAvailableActions = true;

    if(jsonObj.id != systemAccountId && jsonObj.id != adminAccountId) {
        if (jsonObj.accounttype == roleTypeUser || jsonObj.accounttype == roleTypeDomainAdmin) {
            buildActionLinkForTab("Resource limits", accountActionMap, $actionMenu, $midmenuItem1, $detailsTab);
            noAvailableActions = false;
        }

        if(jsonObj.state == "enabled") {
            buildActionLinkForTab("Disable account", accountActionMap, $actionMenu, $midmenuItem1, $detailsTab);
            buildActionLinkForTab("Lock account", accountActionMap, $actionMenu, $midmenuItem1, $detailsTab);
            noAvailableActions = false;
        }
        else if(jsonObj.state == "disabled" || jsonObj.state == "locked") {
            buildActionLinkForTab("Enable account", accountActionMap, $actionMenu, $midmenuItem1, $detailsTab);
            noAvailableActions = false;
        }
    }

    // no available actions
	if(noAvailableActions == true) {
	    $actionMenu.find("#action_list").append($("#no_available_actions").clone().show());
	}
}

var accountActionMap = {
    "Resource limits": {
        dialogBeforeActionFn : doResourceLimitsForAccount
    }
    ,
    "Disable account": {
        isAsyncJob: true,
        asyncJobResponse: "disableaccountresponse",
        dialogBeforeActionFn : doDisableAccount,
        inProcessText: "Disabling account....",
        afterActionSeccessFn: function(json, $midmenuItem1, id) {
            var item = json.queryasyncjobresultresponse.jobresult.account;
            accountToMidmenu(item, $midmenuItem1);
            accountJsonToDetailsTab($midmenuItem1);
        }
    }
    ,
    "Lock account": {
        isAsyncJob: false,
        dialogBeforeActionFn : doLockAccount,
        inProcessText: "Locking account....",
        afterActionSeccessFn: function(json, $midmenuItem1, id) {
            var item = json.lockaccountresponse.account;
            accountToMidmenu(item, $midmenuItem1);
            accountJsonToDetailsTab($midmenuItem1);
        }
    }
    ,
    "Enable account": {
        isAsyncJob: false,
        dialogBeforeActionFn : doEnableAccount,
        inProcessText: "Enabling account....",
        afterActionSeccessFn: function(json, $midmenuItem1, id) {
            var item = json.enableaccountresponse.account;
            accountToMidmenu(item, $midmenuItem1);
            accountJsonToDetailsTab($midmenuItem1);
        }
    }
};

function updateResourceLimitForAccount(domainId, account, type, max) {
	$.ajax({
	    data: createURL("command=updateResourceLimit&domainid="+domainId+"&account="+account+"&resourceType="+type+"&max="+max),
		dataType: "json",
		success: function(json) {
		}
	});
}

function doResourceLimitsForAccount($actionLink, $detailsTab, $midmenuItem1) {
    var $detailsTab = $("#right_panel_content #tab_content_details");
	var jsonObj = $midmenuItem1.data("jsonObj");
	var domainId = jsonObj.domainid;
	var account = jsonObj.name;
	$.ajax({
		cache: false,
		data: createURL("command=listResourceLimits&domainid="+domainId+"&account="+account),
		dataType: "json",
		success: function(json) {
			var limits = json.listresourcelimitsresponse.resourcelimit;
			var preInstanceLimit, preIpLimit, preDiskLimit, preSnapshotLimit, preTemplateLimit = -1;
			if (limits != null) {
				for (var i = 0; i < limits.length; i++) {
					var limit = limits[i];
					switch (limit.resourcetype) {
						case "0":
							preInstanceLimit = limit.max;
							$("#dialog_resource_limits #limits_vm").val(limit.max);
							break;
						case "1":
							preIpLimit = limit.max;
							$("#dialog_resource_limits #limits_ip").val(limit.max);
							break;
						case "2":
							preDiskLimit = limit.max;
							$("#dialog_resource_limits #limits_volume").val(limit.max);
							break;
						case "3":
							preSnapshotLimit = limit.max;
							$("#dialog_resource_limits #limits_snapshot").val(limit.max);
							break;
						case "4":
							preTemplateLimit = limit.max;
							$("#dialog_resource_limits #limits_template").val(limit.max);
							break;
					}
				}
			}
			$("#dialog_resource_limits")
			.dialog('option', 'buttons', {
				"Save": function() {
					// validate values
					var isValid = true;
					isValid &= validateNumber("Instance Limit", $("#dialog_resource_limits #limits_vm"), $("#dialog_resource_limits #limits_vm_errormsg"), -1, 32000, false);
					isValid &= validateNumber("Public IP Limit", $("#dialog_resource_limits #limits_ip"), $("#dialog_resource_limits #limits_ip_errormsg"), -1, 32000, false);
					isValid &= validateNumber("Disk Volume Limit", $("#dialog_resource_limits #limits_volume"), $("#dialog_resource_limits #limits_volume_errormsg"), -1, 32000, false);
					isValid &= validateNumber("Snapshot Limit", $("#dialog_resource_limits #limits_snapshot"), $("#dialog_resource_limits #limits_snapshot_errormsg"), -1, 32000, false);
					isValid &= validateNumber("Template Limit", $("#dialog_resource_limits #limits_template"), $("#dialog_resource_limits #limits_template_errormsg"), -1, 32000, false);
					if (!isValid) return;

					var instanceLimit = trim($("#dialog_resource_limits #limits_vm").val());
					var ipLimit = trim($("#dialog_resource_limits #limits_ip").val());
					var diskLimit = trim($("#dialog_resource_limits #limits_volume").val());
					var snapshotLimit = trim($("#dialog_resource_limits #limits_snapshot").val());
					var templateLimit = trim($("#dialog_resource_limits #limits_template").val());

					$(this).dialog("close");
					if (instanceLimit != preInstanceLimit) {
						updateResourceLimitForAccount(domainId, account, 0, instanceLimit);
					}
					if (ipLimit != preIpLimit) {
						updateResourceLimitForAccount(domainId, account, 1, ipLimit);
					}
					if (diskLimit != preDiskLimit) {
						updateResourceLimitForAccount(domainId, account, 2, diskLimit);
					}
					if (snapshotLimit != preSnapshotLimit) {
						updateResourceLimitForAccount(domainId, account, 3, snapshotLimit);
					}
					if (templateLimit != preTemplateLimit) {
						updateResourceLimitForAccount(domainId, account, 4, templateLimit);
					}
				},
				"Cancel": function() {
					$(this).dialog("close");
				}
			}).dialog("open");
		}
	});
}

function doDisableAccount($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");
    var id = jsonObj.id;

    $("#dialog_disable_account")
    .dialog('option', 'buttons', {
        "Yes": function() {
            $(this).dialog("close");
			var apiCommand = "command=disableAccount&account="+jsonObj.name+"&domainId="+jsonObj.domainid;
	    	doActionToTab(id, $actionLink, apiCommand, $midmenuItem1, $detailsTab) ;
        },
        "Cancel": function() {
            $(this).dialog("close");
        }
    }).dialog("open");
}

function doLockAccount($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");

    $("#dialog_lock_account")
    .dialog('option', 'buttons', {
        "Yes": function() {
            $(this).dialog("close");
			var apiCommand = "command=lockAccount&account="+jsonObj.name+"&domainId="+jsonObj.domainid;
	    	doActionToTab(jsonObj.id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
        },
        "Cancel": function() {
            $(this).dialog("close");
        }
    }).dialog("open");
}

function doEnableAccount($actionLink, $detailsTab, $midmenuItem1) {
    var jsonObj = $midmenuItem1.data("jsonObj");

    $("#dialog_enable_account")
    .dialog('option', 'buttons', {
        "Yes": function() {
            $(this).dialog("close");
			var apiCommand = "command=enableAccount&account="+jsonObj.name+"&domainId="+jsonObj.domainid;
	    	doActionToTab(jsonObj.id, $actionLink, apiCommand, $midmenuItem1, $detailsTab);
        },
        "Cancel": function() {
            $(this).dialog("close");
        }
    }).dialog("open");
}<|endoftext|>


<commit_before>const jwt = require("jwt-simple");
const co = require('co');
const config = require('../config');
const dbX = require('../db');
const coForEach = require('co-foreach');

module.exports = (io) => {
  const collectionVersionsNS = io.of('/collectionVersions');
  collectionVersionsNS.use((socket, next) => {
    let token = socket.handshake.query.token;
    // let isReconnect = socket.handshake.query.isReconnect;
    // console.log('isReconnect:', isReconnect);

    let decoded = null;
    try {
      decoded = jwt.decode(token, config.jwtSecret);
    } catch(error) {
      switch (error) {
        case 'Signature verification failed':
          return next(new Error('authentication error: the jwt has been falsified'));
        case 'Token expired':
          return next(new Error('authentication error: the jwt has been expired'));
      }
    }

    console.log('decoded:', decoded);
    return next();

  })

  //

  collectionVersionsNS.on('connection', (socket) => {
    // const roomId = socket.client.id;
    console.log(`${new Date()}: ${socket.client.id} connected to socket /collectionVersions`);
    socket.on('clientCollectionVersions', (data) => {
      const versionsClient = data['versions'];
      co(function*() {
        const db = yield dbX.dbPromise;
        const versionsLatest = yield db.collection('versions').find({}).toArray();
        const clientCollectionUpdates = {};
        // console.log('versionsClient', versionsClient);
        versionsClient.reduce((acc, curr) => {
          switch (true) {
            case curr['collection'] === 'gd': // prices is called gd at client
              const pricesVersionLatest = versionsLatest.find(v => v['collection'] === 'prices');
              if (curr['version'] !== pricesVersionLatest['version']) {
                acc['gd'] = {version: pricesVersionLatest['version']};
              }
              break;
            default:
              const versionLatest = versionsLatest.find(v => {
                return v['collection'] === curr['collection'];
              });
              if (curr['version'] !== versionLatest['version']) {
                acc[curr['collection']] = {version: versionLatest['version']};
              }
          }
          return acc;
        }, clientCollectionUpdates);
        const hasUpdates = Object.keys(clientCollectionUpdates).length;
        socket.send('anything');
        if (hasUpdates) {

          yield coForEach(Object.keys(clientCollectionUpdates), function*(k) {
            console.log('adding to clientCollectionUpdates:', k);
            switch (k) {
              case 'gd':
              clientCollectionUpdates[k]['data'] = yield db.collection('prices').find({}).toArray();
              break;
            default:
              clientCollectionUpdates[k]['data'] = yield db.collection(k).find({}).toArray();
            }
          });

          // socket.emit('collectionUpdate', clientCollectionUpdates);
          socket.send(clientCollectionUpdates);
        } else {
          socket.send({message: 'all collections up-to-date'});
        }
      }).catch(error => {
        socket.emit('error', {
          error: error.stack
        })
      })
    })
    // after connection, client sends collectionVersions, then server compares

    // each time a collection is updated, update its version in the 'versions' collection

  })
}<commit_msg>debugging 4<commit_after>const jwt = require("jwt-simple");
const co = require('co');
const config = require('../config');
const dbX = require('../db');
const coForEach = require('co-foreach');

module.exports = (io) => {
  const collectionVersionsNS = io.of('/collectionVersions');
  collectionVersionsNS.use((socket, next) => {
    let token = socket.handshake.query.token;
    // let isReconnect = socket.handshake.query.isReconnect;
    // console.log('isReconnect:', isReconnect);

    let decoded = null;
    try {
      decoded = jwt.decode(token, config.jwtSecret);
    } catch(error) {
      switch (error) {
        case 'Signature verification failed':
          return next(new Error('authentication error: the jwt has been falsified'));
        case 'Token expired':
          return next(new Error('authentication error: the jwt has been expired'));
      }
    }

    console.log('decoded:', decoded);
    return next();

  })

  //

  collectionVersionsNS.on('connection', (socket) => {
    // const roomId = socket.client.id;
    console.log(`${new Date()}: ${socket.client.id} connected to socket /collectionVersions`);
    socket.on('clientCollectionVersions', (data) => {
      const versionsClient = data['versions'];
      co(function*() {
        const db = yield dbX.dbPromise;
        const versionsLatest = yield db.collection('versions').find({}).toArray();
        const clientCollectionUpdates = {};
        // console.log('versionsClient', versionsClient);
        versionsClient.reduce((acc, curr) => {
          switch (true) {
            case curr['collection'] === 'gd': // prices is called gd at client
              const pricesVersionLatest = versionsLatest.find(v => v['collection'] === 'prices');
              if (curr['version'] !== pricesVersionLatest['version']) {
                acc['gd'] = {version: pricesVersionLatest['version']};
              }
              break;
            default:
              const versionLatest = versionsLatest.find(v => {
                return v['collection'] === curr['collection'];
              });
              if (curr['version'] !== versionLatest['version']) {
                acc[curr['collection']] = {version: versionLatest['version']};
              }
          }
          return acc;
        }, clientCollectionUpdates);
        const hasUpdates = Object.keys(clientCollectionUpdates).length;
        socket.send('anything');
        if (hasUpdates) {

          yield coForEach(Object.keys(clientCollectionUpdates), function*(k) {
            console.log('adding to clientCollectionUpdates:', k);
            switch (k) {
              case 'gd':
              clientCollectionUpdates[k]['data'] = yield db.collection('prices').find({}).toArray();
              break;
            default:
              clientCollectionUpdates[k]['data'] = yield db.collection(k).find({}).toArray();
            }
          });

          // socket.emit('collectionUpdate', clientCollectionUpdates);
        } else {
          socket.send({message: 'all collections up-to-date'});
        }
      }).catch(error => {
        socket.emit('error', {
          error: error.stack
        })
      })
    })
    // after connection, client sends collectionVersions, then server compares

    // each time a collection is updated, update its version in the 'versions' collection

  })
}<|endoftext|>


<commit_before>//Polygon Triangulation (for top/bottom)
//http://polyk.ivank.net/?p=documentation
//https://github.com/r3mi/poly2tri.js

var fs = require('fs'),
    stl = require('stl');

function createSolidFromLine(p1, p2, h, normal) {
    var x1 = p1[0],
        y1 = p1[1],
        z1 = 0,
        x2 = p2[0],
        y2 = p2[1],
        z2 = 0,
        x3 = p1[0],
        y3 = p1[1],
        z3 = h,
        x4 = p2[0],
        y4 = p2[1],
        z4 = h;

    var verts1 = [
        [x1, y1, z1],
        [x3, y3, z3],
        [x4, y4, z4]
    ];
    var verts2 = [
        [x1, y1, z1],
        [x2, y2, z2],
        [x4, y4, z4]
    ];
    var facets = [{
            normal: normal,
            verts: verts1
    },
        {
            normal: normal,
            verts: verts2
        }];
    return facets;
}
var normal = [0, 0, 0];

var f = new createSolidFromLine([0, 0], [5, 0], 5, normal);

console.log(f[0].verts);
console.log(f[1].verts);
<commit_msg>basic rectangle working, must still be made generic for better usage<commit_after>//Polygon Triangulation (for top/bottom)
//http://polyk.ivank.net/?p=documentation
//https://github.com/r3mi/poly2tri.js

var fs = require('fs'),
    stl = require('stl'),
    poly2tri = require('poly2tri');

//Test array of Points
//Rectangel
var rect = [
    [0, 0],
    [10, 0],
    [10, 10],
    [0, 10]
];
//L Shape
var lShape = [
    [0, 0],
    [10, 0],
    [10, 30],
    [5, 30],
    [5, 10],
    [0, 10]
];
//Cross Product Function to create Normal Vector
function crossProduct(a, b) {
    // Check lengths
    if (a.length != 3 || b.length != 3) {
        return;
    }

    return [a[1] * b[2] - a[2] * b[1],
          a[2] * b[0] - a[0] * b[2],
          a[0] * b[1] - a[1] * b[0]];
}
//function to create Unit vectors from verticies
function createUnitVectors(verts) {
    var x1 = Number(verts[0][0]),
        y1 = Number(verts[0][1]),
        z1 = Number(verts[0][2]),
        x2 = Number(verts[1][0]),
        y2 = Number(verts[1][1]),
        z2 = Number(verts[1][2]),
        x3 = Number(verts[2][0]),
        y3 = Number(verts[2][1]),
        z3 = Number(verts[2][2]);
    var vector1 = [x2 - x1, y2 - y1, z2 - z1],
        vector2 = [x3 - x2, y3 - y2, z3 - z2];
    var length1 = Math.sqrt(Math.pow(vector1[0], 2) + Math.pow(vector1[1], 2) + Math.pow(vector1[2], 2)),
        length2 = Math.sqrt(Math.pow(vector2[0], 2) + Math.pow(vector2[1], 2) + Math.pow(vector2[2], 2));
    vector1 = [vector1[0] / length1, vector1[1] / length1, vector1[2] / length1];
    vector2 = [vector2[0] / length2, vector2[1] / length2, vector2[2] / length2];
    //console.log("v1: " + vector1 + "\nv2 : " + vector2);
    return [vector1, vector2];
}

function createFacet(verts) {
    //var vectors = createUnitVectors(verts);
    //var normal = crossProduct(vectors[0], vectors[1]);
    return {

        verts: verts
    }
}

function createPlane(p1, p2, h) {
    var tri1 = [[p1[0], p1[1], 0], [p2[0], p2[1], 0], [p2[0], p2[1], h]];
    var tri2 = [[p1[0], p1[1], 0], [p1[0], p1[1], h], [p2[0], p2[1], h]];
    var facets = [createFacet(tri1), createFacet(tri2)];
    return facets;
}

var facets = [];
for (var i = 1; i < rect.length; i++) {
    var triangle = createPlane(rect[i - 1], rect[i], 10);
    //console.log(triangle);
    facets.push(triangle[0]);
    facets.push(triangle[1]);
}
var triangle = createPlane(rect[3], rect[0], 10);
facets.push(triangle[0]);
facets.push(triangle[1]);

var contour = [];
rect.forEach(function (point) {
    contour.push(new poly2tri.Point(point[0], point[1]));
});

var swctx = new poly2tri.SweepContext(contour);

swctx.triangulate();
var triangles = swctx.getTriangles();

//Create Bottom Plane
triangles.forEach(function (tri) {
    var verts = [];
    tri.points_.forEach(function (points) {
        verts.push([points.x, points.y, 0]);
    });
    facets.push(createFacet(verts));
});

//Create Top Plane
triangles.forEach(function (tri) {
    var verts = [];
    tri.points_.forEach(function (points) {
        verts.push([points.x, points.y, 10]);
    });
    facets.push(createFacet(verts));
});
console.log(facets);

var stlObj = {
    description: 'modelTest',
    facets: facets
};
fs.writeFileSync('binary.stl', stl.fromObject(stlObj));
<|endoftext|>


<commit_before>function highlightPath(cy, path_uuids){
    cy.startBatch()
    // nodes
    cy.nodes().forEach(function(n){
        var see_thru = true
        var data = n.data()
            // get each node's uuid_list and check if it is in the path
            var uuid_list = n.data().uuid_list
            for (u of uuid_list){
              // if the uuid is in the path, make it not see_thru
              if (path_uuids.indexOf(u) !== -1){
                see_thru = false
                break;
              }
            }
            // if still see_thru, then add the .transparent class
            if (see_thru === true){
              n.addClass('transparentNode')
            }
      })
      cy.edges().forEach(function(e){
          var see_thru = true
          var data = e.data()
          // only act on non-virtual edges, as virtual edges aren't rendered
          if (e.hasClass('virtual') === false){
              // get each edge's uuid_list and check if it is in the path
              var uuid_list = e.data().uuid_list
              for (u of uuid_list){
                // if the uuid is in the path, make it not see_thru
                if (path_uuids.indexOf(u) !== -1){
                  see_thru = false
                  break;
                }
              }
              // if still see_thru, then add the .transparent class
              if (see_thru === true){
                e.addClass('transparentEdge')
              }
            }
        })
  cy.endBatch()
}

// removes .transparent class from all elements in the specified cytoscape obj
function unHighlight(cy){
  cy.startBatch()
  cy.nodes().forEach(function(n){
    n.removeClass('transparent')
      if (n.hasClass('transparentNode') === false){
      }
  })
  cy.edges().forEach(function(e){
    e.removeClass('transparent')
    if (e.hasClass('transparentEdge') === false){
    }
  })
  cy.endBatch()
}
<commit_msg>Lint highlightPath<commit_after>function highlightPath(cy, path_uuids){
    cy.startBatch();
    // nodes
    cy.nodes().forEach(function(n){
        var see_thru = true;
        var data = n.data();
            // get each node's uuid_list and check if it is in the path
            var uuid_list = n.data().uuid_list;
            for (var u of uuid_list){
              // if the uuid is in the path, make it not see_thru
              if (path_uuids.indexOf(u) !== -1){
                see_thru = false;
                break;
              }
            }
            // if still see_thru, then add the .transparent class
            if (see_thru === true){
              n.addClass('transparentNode');
            }
      });
      cy.edges().forEach(function(e){
          var see_thru = true;
          var data = e.data();
          // only act on non-virtual edges, as virtual edges aren't rendered
          if (e.hasClass('virtual') === false){
              // get each edge's uuid_list and check if it is in the path
              var uuid_list = e.data().uuid_list;
              for (var u of uuid_list){
                // if the uuid is in the path, make it not see_thru
                if (path_uuids.indexOf(u) !== -1){
                  see_thru = false;
                  break;
                }
              }
              // if still see_thru, then add the .transparent class
              if (see_thru === true){
                e.addClass('transparentEdge');
              }
            }
        });
  cy.endBatch();
}

// removes .transparent class from all elements in the specified cytoscape obj
function unHighlight(cy){
  cy.startBatch();
  cy.nodes().forEach(function(n){
    n.removeClass('transparent');
      if (n.hasClass('transparentNode') === false){
      }
  });
  cy.edges().forEach(function(e){
    e.removeClass('transparent');
    if (e.hasClass('transparentEdge') === false){
    }
  });
  cy.endBatch();
}
<|endoftext|>


<commit_before>import React, { useState, useEffect, useCallback } from 'react'
import styled from '@emotion/styled'
import { useSelector } from 'react-redux'
import useLocalStorageState from 'use-local-storage-state'

import history from '../history'
import ZoomableImage from './ZoomableImage'
import PhotoMetadata from './PhotoMetadata'
import { getSafeArea } from '../stores/layout/selector'
import { getPrevNextPhotos } from '../stores/photos/selector'

import { ReactComponent as ArrowBackIcon } from '../static/images/arrow_back.svg'
import { ReactComponent as ArrowLeftIcon } from '../static/images/arrow_left.svg'
import { ReactComponent as ArrowRightIcon } from '../static/images/arrow_right.svg'
import { ReactComponent as InfoIcon } from '../static/images/info.svg'
import { ReactComponent as CloseIcon } from '../static/images/close.svg'

// const I_KEY = 73
const LEFT_KEY = 37
const RIGHT_KEY = 39

const Container = styled('div')`
  width: 100vw;
  height: 100vh;
  background-color: #1b1b1b;

  .content {
    width: 110vw;
    height: 100vh;
    overflow: auto;
    position: fixed;
    z-index: 10;
    top: 0;
    left: 0;
  }

  .backIcon {
    position: absolute;
    top: 10px;
    left: 10px;
    cursor: pointer;
    z-index: 10;
    svg {
      filter: invert(0.9);
    }
  }
  .prevNextIcons {
    position: absolute;
    top: 0;
    width: 100%;
    display: flex;
    justify-content: space-between;
    opacity: 0;
    transition: opacity 250ms;
    svg {
      filter: invert(0.9);
      cursor: pointer;
      padding: 10vh 10px;
      width: 48px;
      height: 25vh;
      position: absolute;
      top: 37.5vh;
      &.prevArrow {
        left: 0;
      }
      &.nextArrow {
        right: 0;
      }
    }
  }
  .showDetailIcon {
    position: absolute;
    right: 10px;
    top: 10px;
    filter: invert(0.9);
    cursor: pointer;
    z-index: 10;
  }

  /* When two boxes can no longer fit next to each other */
  @media all and (max-width: 500px) {
    .metadata .boxes .box {
      width: 100%;
    }
    .metadata .boxes .histogram {
      margin-right: 40px;
    }
    .metadata .boxes .map {
      margin-right: 40px;
    }
  }
`

const PhotoDetail = ({ photoId, photo, refetch, updatePhotoFile }) => {
  const safeArea = useSelector(getSafeArea)
  const [showBoundingBox, setShowBoundingBox] = useLocalStorageState(
    'showObjectBoxes',
    true
  )
  const [showMetadata, setShowMetadata] = useState(false)
  const [showPrevNext, setShowPrevNext] = useState(false)
  const prevNextPhotos = useSelector((state) =>
    getPrevNextPhotos(state, photoId)
  )

  // TODO: Bring this back so it doesn't get triggered by someone adding a tag with 'i' in it
  // useEffect(() => {
  //   const handleKeyDown = (event) => {
  //     switch (event.keyCode) {
  //       case I_KEY:
  //         setShowMetadata(!showMetadata)
  //         break
  //       default:
  //         break
  //     }
  //   }

  //   document.addEventListener('keydown', handleKeyDown)

  //   return () => {
  //     document.removeEventListener('keydown', handleKeyDown)
  //   }
  // }, [showMetadata])

  const prevPhoto = useCallback(() => {
    let id = prevNextPhotos.prev[0]
    id && history.push(`/photo/${id}`)
  }, [prevNextPhotos])
  const nextPhoto = useCallback(() => {
    let id = prevNextPhotos.next[0]
    id && history.push(`/photo/${id}`)
  }, [prevNextPhotos])

  useEffect(() => {
    const handleKeyDown = (event) => {
      switch (event.keyCode) {
        case LEFT_KEY:
          prevPhoto()
          break
        case RIGHT_KEY:
          nextPhoto()
          break
        default:
          break
      }
    }

    document.addEventListener('keydown', handleKeyDown)

    return () => {
      document.removeEventListener('keydown', handleKeyDown)
    }
  }, [photoId, prevNextPhotos, prevPhoto, nextPhoto])

  let boxes = photo?.objectTags.map((objectTag) => {
    return {
      name: objectTag.tag.name,
      positionX: objectTag.positionX,
      positionY: objectTag.positionY,
      sizeX: objectTag.sizeX,
      sizeY: objectTag.sizeY,
    }
  })

  return (
    <Container>
      <ZoomableImage
        photoId={photoId}
        boxes={showBoundingBox && boxes}
        next={nextPhoto}
        prev={prevPhoto}
      />

        <ArrowBackIcon alt="Close" onClick={() => history.push('/')} />

        <ArrowLeftIcon
          alt="Previous"
          className="prevArrow"
          onClick={prevPhoto}
          onMouseOver={() => setShowPrevNext(true)}
          onMouseOut={() => setShowPrevNext(false)}
          title="Use [←] left/right [→] arrow keys to quickly go to the previous/next photo"
        />
        <ArrowRightIcon
          alt="Previous"
          className="nextArrow"
          onClick={nextPhoto}
          onMouseOver={() => setShowPrevNext(true)}
          onMouseOut={() => setShowPrevNext(false)}
          title="Use [←] left/right [→] arrow keys to quickly go to the previous/next photo"
        />

      {photo && (
        <PhotoMetadata
          photo={photo}
          show={showMetadata}
          refetch={refetch}
          showBoundingBox={showBoundingBox}
          setShowBoundingBox={setShowBoundingBox}
          updatePhotoFile={updatePhotoFile}
        />
      )}
      {!showMetadata ? (
        <InfoIcon
          className="showDetailIcon"
          height="30"
          width="30"
          onClick={() => setShowMetadata(!showMetadata)}
          style={{ marginTop: safeArea.top }}
          // title="Press [I] key to show/hide photo details"
        />
      ) : (
        <CloseIcon
          className="showDetailIcon"
          height="30"
          width="30"
          onClick={() => setShowMetadata(!showMetadata)}
          style={{ marginTop: safeArea.top }}
          // title="Press [I] key to show/hide photo details"
        />
      )}
    </Container>
  )
}

export default PhotoDetail
<commit_msg>Improvements to back button<commit_after>import React, { useState, useEffect, useCallback } from 'react'
import styled from '@emotion/styled'
import { useSelector } from 'react-redux'
import useLocalStorageState from 'use-local-storage-state'

import history from '../history'
import ZoomableImage from './ZoomableImage'
import PhotoMetadata from './PhotoMetadata'
import { getSafeArea } from '../stores/layout/selector'
import { getPrevNextPhotos } from '../stores/photos/selector'

import { ReactComponent as ArrowBackIcon } from '../static/images/arrow_back.svg'
import { ReactComponent as ArrowLeftIcon } from '../static/images/arrow_left.svg'
import { ReactComponent as ArrowRightIcon } from '../static/images/arrow_right.svg'
import { ReactComponent as InfoIcon } from '../static/images/info.svg'
import { ReactComponent as CloseIcon } from '../static/images/close.svg'

// const I_KEY = 73
const LEFT_KEY = 37
const RIGHT_KEY = 39

const Container = styled('div')`
  width: 100vw;
  height: 100vh;
  background-color: #1b1b1b;

  .content {
    width: 110vw;
    height: 100vh;
    overflow: auto;
    position: fixed;
    z-index: 10;
    top: 0;
    left: 0;
  }

  .backIcon {
    position: absolute;
    top: 10px;
    left: 10px;
    cursor: pointer;
    z-index: 10;
    svg {
      filter: invert(0.9);
    }
  }
  .prevNextIcons {
    position: absolute;
    top: 0;
    width: 100%;
    display: flex;
    justify-content: space-between;
    opacity: 0;
    transition: opacity 250ms;
    svg {
      filter: invert(0.9);
      cursor: pointer;
      padding: 10vh 10px;
      width: 48px;
      height: 25vh;
      position: absolute;
      top: 37.5vh;
      &.prevArrow {
        left: 0;
      }
      &.nextArrow {
        right: 0;
      }
    }
  }
  .showDetailIcon {
    position: absolute;
    right: 10px;
    top: 10px;
    filter: invert(0.9);
    cursor: pointer;
    z-index: 10;
  }

  /* When two boxes can no longer fit next to each other */
  @media all and (max-width: 500px) {
    .metadata .boxes .box {
      width: 100%;
    }
    .metadata .boxes .histogram {
      margin-right: 40px;
    }
    .metadata .boxes .map {
      margin-right: 40px;
    }
  }
`

const PhotoDetail = ({ photoId, photo, refetch, updatePhotoFile }) => {
  const safeArea = useSelector(getSafeArea)
  const [showBoundingBox, setShowBoundingBox] = useLocalStorageState(
    'showObjectBoxes',
    true
  )
  const [showMetadata, setShowMetadata] = useState(false)
  const [showPrevNext, setShowPrevNext] = useState(false)
  const prevNextPhotos = useSelector((state) =>
    getPrevNextPhotos(state, photoId)
  )
  const [numHistoryPushes, setNumHistoryPushes] = useState(0)

  // TODO: Bring this back so it doesn't get triggered by someone adding a tag with 'i' in it
  // useEffect(() => {
  //   const handleKeyDown = (event) => {
  //     switch (event.keyCode) {
  //       case I_KEY:
  //         setShowMetadata(!showMetadata)
  //         break
  //       default:
  //         break
  //     }
  //   }

  //   document.addEventListener('keydown', handleKeyDown)

  //   return () => {
  //     document.removeEventListener('keydown', handleKeyDown)
  //   }
  // }, [showMetadata])

  const prevPhoto = useCallback(() => {
    let id = prevNextPhotos.prev[0]
    if (id) {
      history.push(`/photo/${id}`)
      setNumHistoryPushes(numHistoryPushes + 1)
    }
  }, [prevNextPhotos])
  const nextPhoto = useCallback(() => {
    let id = prevNextPhotos.next[0]
    if (id) {
      history.push(`/photo/${id}`)
      setNumHistoryPushes(numHistoryPushes + 1)
    }
  }, [prevNextPhotos])

  useEffect(() => {
    const handleKeyDown = (event) => {
      switch (event.keyCode) {
        case LEFT_KEY:
          prevPhoto()
          break
        case RIGHT_KEY:
          nextPhoto()
          break
        default:
          break
      }
    }

    document.addEventListener('keydown', handleKeyDown)

    return () => {
      document.removeEventListener('keydown', handleKeyDown)
    }
  }, [photoId, prevNextPhotos, prevPhoto, nextPhoto])

  let boxes = photo?.objectTags.map((objectTag) => {
    return {
      name: objectTag.tag.name,
      positionX: objectTag.positionX,
      positionY: objectTag.positionY,
      sizeX: objectTag.sizeX,
      sizeY: objectTag.sizeY,
    }
  })

  return (
    <Container>
      <ZoomableImage
        photoId={photoId}
        boxes={showBoundingBox && boxes}
        next={nextPhoto}
        prev={prevPhoto}
      />

        <ArrowBackIcon
          alt="Close"
          onClick={() => {
            if (
              history.length - numHistoryPushes > 2 ||
              document.referrer != ''
            ) {
              history.go(-(numHistoryPushes + 1))
            } else {
              history.push('/')
            }
          }}
        />

        <ArrowLeftIcon
          alt="Previous"
          className="prevArrow"
          onClick={prevPhoto}
          onMouseOver={() => setShowPrevNext(true)}
          onMouseOut={() => setShowPrevNext(false)}
          title="Use [←] left/right [→] arrow keys to quickly go to the previous/next photo"
        />
        <ArrowRightIcon
          alt="Previous"
          className="nextArrow"
          onClick={nextPhoto}
          onMouseOver={() => setShowPrevNext(true)}
          onMouseOut={() => setShowPrevNext(false)}
          title="Use [←] left/right [→] arrow keys to quickly go to the previous/next photo"
        />

      {photo && (
        <PhotoMetadata
          photo={photo}
          show={showMetadata}
          refetch={refetch}
          showBoundingBox={showBoundingBox}
          setShowBoundingBox={setShowBoundingBox}
          updatePhotoFile={updatePhotoFile}
        />
      )}
      {!showMetadata ? (
        <InfoIcon
          className="showDetailIcon"
          height="30"
          width="30"
          onClick={() => setShowMetadata(!showMetadata)}
          style={{ marginTop: safeArea.top }}
          // title="Press [I] key to show/hide photo details"
        />
      ) : (
        <CloseIcon
          className="showDetailIcon"
          height="30"
          width="30"
          onClick={() => setShowMetadata(!showMetadata)}
          style={{ marginTop: safeArea.top }}
          // title="Press [I] key to show/hide photo details"
        />
      )}
    </Container>
  )
}

export default PhotoDetail
<|endoftext|>


<commit_before>var alloy = require('../.alloy');
var gulp = require('gulp');
var path = require('path');
var replace = require('gulp-replace');
var run = require('run-sequence');
var spawn = require('spawn-local-bin');

var ROOT = path.join(__dirname, '..');

gulp.task('init', function(callback) {
    run('init-bower', 'init-yui', callback);
});

gulp.task('init-bower', function(callback) {
    var args = ['install'];
    var cmd = 'bower';
    var cwd = ROOT;

    spawn(cmd, args, cwd)
        .on('exit', function() {
            callback();
        });
});

gulp.task('init-yui', function() {
    return gulp.src('bower_components/yui3/build/**', { cwd: ROOT })
        .pipe(replace('@VERSION@', alloy.yuiversion))
        .pipe(gulp.dest('build', { cwd: ROOT }));
});<commit_msg>AUI-1446 Make sure all npm dependencies are updated on init<commit_after>var alloy = require('../.alloy');
var gulp = require('gulp');
var path = require('path');
var replace = require('gulp-replace');
var run = require('run-sequence');
var spawn = require('spawn-local-bin');

var ROOT = path.join(__dirname, '..');

gulp.task('init', function(callback) {
    run('init-bower', 'init-npm', 'init-yui', callback);
});

gulp.task('init-bower', function(callback) {
    var args = ['install'];
    var cmd = 'bower';
    var cwd = ROOT;

    spawn(cmd, args, cwd)
        .on('exit', function() {
            callback();
        });
});

gulp.task('init-npm', function(callback) {
    var args = ['install'];
    var cmd = 'npm';
    var cwd = ROOT;

    spawn(cmd, args, cwd)
        .on('exit', function() {
            callback();
        });
});

gulp.task('init-yui', function() {
    return gulp.src('bower_components/yui3/build/**', { cwd: ROOT })
        .pipe(replace('@VERSION@', alloy.yuiversion))
        .pipe(gulp.dest('build', { cwd: ROOT }));
});<|endoftext|>


<commit_before>const Discord = require("discord.js");
const util = require("../util");
const configuration = require("../configuration");
const ContentRegExpHandler = require("../content-regexp-handler.js");

const CSGO_ICON_URL = configuration.getConfig("gather.csgo.image",
    "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/730/d0595ff02f5c79fd19b06f4d6165c3fda2372820.jpg");
const LOL_ICON_URL = configuration.getConfig("gather.lol.image",
    "http://vignette1.wikia.nocookie.net/leagueoflegends/images/8/86/League_of_legends_logo_transparent.png");
const DEFAULT_CSGO_TITLE = configuration.getConfig("gather.csgo.title", "CS:GO Matchmaking?");
const DEFAULT_LOL_TITLE = configuration.getConfig("gather.lol.title", "LoL Ranked?");

var joinListener = function (storage, message) {
    let starterMessage = storage.getFromChannelLevel(message.channel, "gather.starterMessage");
    let playerList = storage.getFromChannelLevel(message.channel, "gather.playerList", true, []);
    let currentRichEmbed = storage.getFromChannelLevel(message.channel, "gather.currentRichEmbed");
    let gatherMessages = storage.getFromChannelLevel(message.channel, "gather.gatherMessages", true, []);

    if (starterMessage && message.channel === starterMessage.channel) {
        if (message.content === "+" && !playerList.includes(message.author)) {
            playerList.push(message.author);

            currentRichEmbed.addField("\u200B", playerList.length + " - " + message.author);

            gatherMessages.forEach(gM => gM.edit(currentRichEmbed));

            message.delete(2000);

            storage.saveOnChannelLevel(message.channel, "gather", {
                playerList: playerList,
                currentRichEmbed: currentRichEmbed
            });
        }
    }
};

function sendGatherStatus(message, storage) {
    let currentRichEmbed = storage.getFromChannelLevel(message.channel, "gather.currentRichEmbed");
    if (!currentRichEmbed) {
        let title = "";
        if (message.content.split(" ").length > 1) {
            title = message.content.split(" ").slice(1).join(" ");
        } else {
            if (storage.getFromChannelLevel(message.channel, "gather.isCSGO")) {
                title = DEFAULT_CSGO_TITLE;
            } else if (storage.getFromChannelLevel(message.channel, "gather.isLoL")) {
                title = DEFAULT_LOL_TITLE;
            }
        }

        currentRichEmbed = new Discord.RichEmbed()
            .setAuthor(storage.getFromChannelLevel(message.channel, "gather.starterMessage.author.username"),
                storage.getFromChannelLevel(message.channel, "gather.starterMessage.author.avatarURL"))
            .setTitle(title)
            .setDescription("Type '+' to join this gathering!")
            .setColor([255, 0, 0]);

        if (storage.getFromChannelLevel(message.channel, "gather.isCSGO")) {
            currentRichEmbed.setThumbnail(CSGO_ICON_URL);
        } else if (storage.getFromChannelLevel(message.channel, "gather.isLoL")) {
            currentRichEmbed.setThumbnail(LOL_ICON_URL);
        }

        storage.saveOnChannelLevel(message.channel, "gather", {
            currentRichEmbed: currentRichEmbed
        });
    }

    message.channel.send(currentRichEmbed)
        .then(m => {
            let gatherMessages = storage.getFromChannelLevel(message.channel, "gather.gatherMessages", true, []);
            gatherMessages.push(m);
            storage.saveOnChannelLevel(message.channel, "gather", {
                gatherMessages: gatherMessages
            });
        });
}

function clearGathering(message, storage) {
    if (!storage.getFromChannelLevel(message.channel, "gather.isGathering")) {
        return;
    }

    message.client.removeListener("message", storage.getFromChannelLevel(message.channel, "gather.joinListener"));

    if (storage.getFromChannelLevel(message.channel, "gather.endTimeout")) {
        clearTimeout(storage.getFromChannelLevel(message.channel, "gather.endTimeout"));
    }

    let currentRichEmbed = storage.getFromChannelLevel(message.channel, "gather.currentRichEmbed");

    currentRichEmbed.setFooter("ENDED!");
    message.channel.send(currentRichEmbed);
    storage.getFromChannelLevel(message.channel, "gather.gatherMessages")
        .forEach(gM => gM.edit(currentRichEmbed));

    storage.saveOnChannelLevel(message.channel, "gather", {
        starterMessage: undefined,
        currentRichEmbed: undefined,
        endTimeout: undefined,
        playerList: [],
        gatherMessages: [],
        customGame: "",
        isGathering: false
    });
}

function doGather(message, storage) {
    let listener = joinListener.bind(this, storage);

    storage.saveOnChannelLevel(message.channel, "gather", {
        starterMessage: message,
        isGathering: true,
        // 15 min timeout to auto-stop gathering
        endTimeout: setTimeout(clearGathering, 1000 * 60 * 15, message, storage),
        joinListener: listener
    });

    sendGatherStatus(message, storage);

    message.client.on("message", listener);
}

const GatherEndHandler = {
    GatherEndHandler() {
        this.ContentRegExpHandler(/^\.gatherend/);
    },
    handle(message, storage) {
        clearGathering(message, storage);
    }
};

Object.setPrototypeOf(GatherEndHandler, ContentRegExpHandler);

const GatherHandler = {
    GatherHandler() {
        this.ContentRegExpHandler(/^\.gather/);
    },
    handle(message, storage) {
        let params = util.sanatizeCommandInput(message.content.split(" "));

        if (storage.getFromChannelLevel(message.channel, "gather.isGathering")) {
            sendGatherStatus(message, storage);
        } else {
            if (params.length >= 2) {
                doGather(message, storage);
            } else {
                message.channel.send("Invalid command! .gather Question?");
            }
        }
    }
};

Object.setPrototypeOf(GatherHandler, ContentRegExpHandler);

/*
const CsgoHandler = {
    CsgoHandler() {
        this.ContentRegExpHandler(/^\.csgo\?/);
    },
    handle(message, storage) {
        if (storage.getFromChannelLevel(message.channel, "gather.isGathering")) {
            sendGatherStatus(message, storage);
        } else {
            storage.saveOnChannelLevel(message.channel, "gather", {
                isCSGO: true
            });

            doGather(message, storage);
        }
    }
};

Object.setPrototypeOf(CsgoHandler, ContentRegExpHandler);

const LolHandler = {
    LolHandler() {
        this.ContentRegExpHandler(/^\.lol\?/);
    },
    handle(message, storage) {
        if (storage.getFromChannelLevel(message.channel, "gather.isGathering")) {
            sendGatherStatus(message, storage);
        } else {
            storage.saveOnChannelLevel(message.channel, "gather", {
                isLoL: true
            });

            doGather(message, storage);
        }
    }
};

Object.setPrototypeOf(LolHandler, ContentRegExpHandler);
*/

const CustomGameGatherHandler = {
    REGEXP: /^\.(.+)\?/,
    CustomGameGatherHandler() {
        this.ContentRegExpHandler(this.REGEXP);
    },
    handle(message, storage) {
        if (storage.getFromChannelLevel(message.channel, "gather.isGathering")) {
            sendGatherStatus(message, storage);
        } else {
            storage.saveOnChannelLevel(message.channel, "gather", {
                customGame: this.REGEXP.exec(message)[0]
            });

            doGather(message, storage);
        }
    }
};

Object.setPrototypeOf(CustomGameGatherHandler, ContentRegExpHandler);

function registerHandlers(registerFunction) {
    const gatherEndHandler = Object.create(GatherEndHandler);
    gatherEndHandler.GatherEndHandler();

    const gatherHandler = Object.create(GatherHandler);
    gatherHandler.GatherHandler();

    /*
    const csgoHandler = Object.create(CsgoHandler);
    csgoHandler.CsgoHandler();

    const lolHandler = Object.create(LolHandler);
    lolHandler.LolHandler();
    */

    registerFunction(gatherEndHandler);
    registerFunction(gatherHandler);

    /*
    registerFunction(csgoHandler);
    registerFunction(lolHandler);
    */
}

module.exports = {
    registerHandlers
};
<commit_msg>Configurable custom gather commands<commit_after>const Discord = require("discord.js");
const util = require("../util");
const configuration = require("../configuration");
const ContentRegExpHandler = require("../content-regexp-handler.js");

var joinListener = function (storage, message) {
    let starterMessage = storage.getFromChannelLevel(message.channel, "gather.starterMessage");
    let playerList = storage.getFromChannelLevel(message.channel, "gather.playerList", true, []);
    let currentRichEmbed = storage.getFromChannelLevel(message.channel, "gather.currentRichEmbed");
    let gatherMessages = storage.getFromChannelLevel(message.channel, "gather.gatherMessages", true, []);

    if (starterMessage && message.channel === starterMessage.channel) {
        if (message.content === "+" && !playerList.includes(message.author)) {
            playerList.push(message.author);

            currentRichEmbed.addField("\u200B", playerList.length + " - " + message.author);

            gatherMessages.forEach(gM => gM.edit(currentRichEmbed));

            message.delete(2000);

            storage.saveOnChannelLevel(message.channel, "gather", {
                playerList: playerList,
                currentRichEmbed: currentRichEmbed
            });
        }
    }
};

function sendGatherStatus(message, storage) {
    let currentRichEmbed = storage.getFromChannelLevel(message.channel, "gather.currentRichEmbed");
    if (!currentRichEmbed) {
        currentRichEmbed = new Discord.RichEmbed()
            .setAuthor(storage.getFromChannelLevel(message.channel, "gather.starterMessage.author.username"),
                storage.getFromChannelLevel(message.channel, "gather.starterMessage.author.avatarURL"))
            .setDescription("Type '+' to join this gathering!")
            .setColor([255, 0, 0]);

        let customGameKeyword = storage.getFromChannelLevel(message.channel, "gather.customGame");
        let customGameObject = configuration.getConfig("gather." + customGameKeyword, undefined);
        if (customGameObject != undefined) {
            currentRichEmbed.setTitle(customGameObject.title);
            currentRichEmbed.setThumbnail(customGameObject.thumbnail);
        }

        if (message.content.split(" ").length > 1) {
            currentRichEmbed.setTitle(message.content.split(" ").slice(1).join(" "));
        }

        storage.saveOnChannelLevel(message.channel, "gather", {
            currentRichEmbed: currentRichEmbed
        });
    }

    message.channel.send(currentRichEmbed)
        .then(m => {
            let gatherMessages = storage.getFromChannelLevel(message.channel, "gather.gatherMessages", true, []);
            gatherMessages.push(m);
            storage.saveOnChannelLevel(message.channel, "gather", {
                gatherMessages: gatherMessages
            });
        });
}

function clearGathering(message, storage) {
    if (!storage.getFromChannelLevel(message.channel, "gather.isGathering")) {
        return;
    }

    message.client.removeListener("message", storage.getFromChannelLevel(message.channel, "gather.joinListener"));

    if (storage.getFromChannelLevel(message.channel, "gather.endTimeout")) {
        clearTimeout(storage.getFromChannelLevel(message.channel, "gather.endTimeout"));
    }

    let currentRichEmbed = storage.getFromChannelLevel(message.channel, "gather.currentRichEmbed");

    currentRichEmbed.setFooter("ENDED!");
    message.channel.send(currentRichEmbed);
    storage.getFromChannelLevel(message.channel, "gather.gatherMessages")
        .forEach(gM => gM.edit(currentRichEmbed));

    storage.saveOnChannelLevel(message.channel, "gather", {
        starterMessage: undefined,
        currentRichEmbed: undefined,
        endTimeout: undefined,
        playerList: [],
        gatherMessages: [],
        customGame: "",
        isGathering: false
    });
}

function doGather(message, storage) {
    let listener = joinListener.bind(this, storage);

    storage.saveOnChannelLevel(message.channel, "gather", {
        starterMessage: message,
        isGathering: true,
        // 15 min timeout to auto-stop gathering
        endTimeout: setTimeout(clearGathering, 1000 * 60 * 15, message, storage),
        joinListener: listener
    });

    sendGatherStatus(message, storage);

    message.client.on("message", listener);
}

const GatherEndHandler = {
    GatherEndHandler() {
        this.ContentRegExpHandler(/^\.gatherend/);
    },
    handle(message, storage) {
        clearGathering(message, storage);
    }
};

Object.setPrototypeOf(GatherEndHandler, ContentRegExpHandler);

const GatherHandler = {
    GatherHandler() {
        this.ContentRegExpHandler(/^\.gather/);
    },
    handle(message, storage) {
        let params = util.sanatizeCommandInput(message.content.split(" "));

        if (storage.getFromChannelLevel(message.channel, "gather.isGathering")) {
            sendGatherStatus(message, storage);
        } else {
            if (params.length >= 2) {
                doGather(message, storage);
            } else {
                message.channel.send("Invalid command! .gather Question?");
            }
        }
    }
};

Object.setPrototypeOf(GatherHandler, ContentRegExpHandler);

/*
const CsgoHandler = {
    CsgoHandler() {
        this.ContentRegExpHandler(/^\.csgo\?/);
    },
    handle(message, storage) {
        if (storage.getFromChannelLevel(message.channel, "gather.isGathering")) {
            sendGatherStatus(message, storage);
        } else {
            storage.saveOnChannelLevel(message.channel, "gather", {
                isCSGO: true
            });

            doGather(message, storage);
        }
    }
};

Object.setPrototypeOf(CsgoHandler, ContentRegExpHandler);

const LolHandler = {
    LolHandler() {
        this.ContentRegExpHandler(/^\.lol\?/);
    },
    handle(message, storage) {
        if (storage.getFromChannelLevel(message.channel, "gather.isGathering")) {
            sendGatherStatus(message, storage);
        } else {
            storage.saveOnChannelLevel(message.channel, "gather", {
                isLoL: true
            });

            doGather(message, storage);
        }
    }
};

Object.setPrototypeOf(LolHandler, ContentRegExpHandler);
*/

const CustomGameGatherHandler = {
    REGEXP: /^\.(.+)\?/,
    CustomGameGatherHandler() {
        this.ContentRegExpHandler(this.REGEXP);
    },
    handle(message, storage) {
        let keyword = this.REGEXP.exec(message)[1];
        if (configuration.getConfig("gather." + keyword, undefined) != undefined) {
            if (storage.getFromChannelLevel(message.channel, "gather.isGathering")) {
                sendGatherStatus(message, storage);
            } else {
                storage.saveOnChannelLevel(message.channel, "gather", {
                    customGame: keyword
                });

                doGather(message, storage);
            }
        }
    }
};

Object.setPrototypeOf(CustomGameGatherHandler, ContentRegExpHandler);

function registerHandlers(registerFunction) {
    const gatherEndHandler = Object.create(GatherEndHandler);
    gatherEndHandler.GatherEndHandler();

    const gatherHandler = Object.create(GatherHandler);
    gatherHandler.GatherHandler();

    const customGameGatherHandler = Object.create(CustomGameGatherHandler);
    customGameGatherHandler.CustomGameGatherHandler();

    /*
    const csgoHandler = Object.create(CsgoHandler);
    csgoHandler.CsgoHandler();

    const lolHandler = Object.create(LolHandler);
    lolHandler.LolHandler();
    */

    registerFunction(gatherEndHandler);
    registerFunction(gatherHandler);
    registerFunction(customGameGatherHandler);

    /*
    registerFunction(csgoHandler);
    registerFunction(lolHandler);
    */
}

module.exports = {
    registerHandlers
};
<|endoftext|>


<commit_before>$(function(){
    $('.formkit-date').datepicker().each(function(){
        $(this).datepicker('option', 'dateFormat', $(this).data('format'));
    });
});
<commit_msg>jsdate simpler<commit_after>$(function(){
    $('.formkit-date').each(function(){
        $(this).datepicker({
            dateFormat: $(this).data('format')
        });
    });
});
<|endoftext|>