<commit_before>require('./helper');
var Route = require('../modules/components/Route');

var App = React.createClass({
  displayName: 'App',
  render: function () {
    return React.DOM.div();
  }
});

describe('a Route that matches the URL', function () {
  it('returns an array', function () {
    var route = Route({ path: '/a/b/c', handler: App });

    var matches = route.match('/a/b/c');
    assert(matches);
    expect(matches.length).toEqual(1);

    var rootMatch = getRootMatch(matches);
    expect(rootMatch.params).toEqual({});
  });

  describe('that contains dynamic segments', function () {
    it('returns an array with the correct params', function () {
      var route = Route({ path: '/posts/:id/edit', handler: App });

      var matches = route.match('/posts/abc/edit');
      assert(matches);
      expect(matches.length).toEqual(1);

      var rootMatch = getRootMatch(matches);
      expect(rootMatch.params).toEqual({ id: 'abc' });
    });
  });
});

describe('a Route that does not match the URL', function () {
  it('returns null', function () {
    var route = Route({ path: '/a/b/c', handler: App });
    expect(route.match('/not-found')).toBe(null);
  });
});

describe('a nested Route that matches the URL', function () {
  it('returns the appropriate params for each match', function () {
    var route = Route({ name: 'posts', path: '/posts/:id', handler: App },
      Route({ name: 'comment', path: '/posts/:id/comments/:commentId', handler: App })
    );

    var matches = route.match('/posts/abc/comments/123');
    assert(matches);
    expect(matches.length).toEqual(2);

    var rootMatch = getRootMatch(matches);
    expect(rootMatch.route.props.name).toEqual('comment');
    expect(rootMatch.params).toEqual({ id: 'abc', commentId: '123' });

    var firstMatch = matches[0];
    expect(firstMatch.route.props.name).toEqual('posts');
    expect(firstMatch.params).toEqual({ id: 'abc' });
  });
});

describe('multiple nested Router that match the URL', function () {
  it('returns the first one in the subtree, depth-first', function () {
    var route = Route({ path: '/', handler: App },
      Route({ path: '/a', handler: App },
        Route({ path: '/a/b', name: 'expected', handler: App })
      ),
      Route({ path: '/a/b', handler: App })
    );

    var matches = route.match('/a/b');
    assert(matches);
    expect(matches.length).toEqual(3);

    var rootMatch = getRootMatch(matches);
    expect(rootMatch.route.props.name).toEqual('expected');
  });
});

describe('a Route with custom props', function() {
  it('receives props', function (done) {
    var route = Route({ handler: App, customProp: 'prop' });
    var component = TestUtils.renderIntoDocument(route);

    route.dispatch('/').then(function () {
      assert(component.props.customProp);
      expect(component.props.customProp).toEqual('prop');
      done();
    });
  });
});

// describe('a Router that renders on the server', function() {
//   it('works with async willTransitionTo()', function(done) {
//     var dataStore = 'goodbye';
//     var Layout = React.createClass({
//       render: function() {
//         return React.DOM.article(null, this.props.activeRoute);
//       }
//     });
//     var AsyncApp = React.createClass({
//       displayName: 'AsyncApp',
//       statics: {
//         willTransitionTo: function() {
//           return new Promise(function(resolve) {
//             setTimeout(function() {
//               dataStore = 'hello';
//               resolve();
//             }, 0);
//           });
//         }
//       },
//       render: function() {
//         return React.DOM.div(null, dataStore);
//       }
//     });

//     var router = Router(
//       RouteComponent({ path: '/', handler: Layout},
//         RouteComponent({ path: '/a', handler: AsyncApp }))
//     );

//     router.renderComponentToString('/a').then(function(result) {
//       expect(result.indexOf('div') > -1).toBe(true);
//       expect(result.indexOf('hello') > -1).toBe(true);

//       done();
//     });
//   });
// });

function getRootMatch(matches) {
  return matches[matches.length - 1];
}
<commit_msg>pending tests<commit_after>require('./helper');
var Route = require('../modules/components/Route');

var App = React.createClass({
  displayName: 'App',
  render: function () {
    return React.DOM.div();
  }
});

describe('a Route that matches the URL', function () {
  it('returns an array', function () {
    var route = Route({ path: '/a/b/c', handler: App });

    var matches = route.match('/a/b/c');
    assert(matches);
    expect(matches.length).toEqual(1);

    var rootMatch = getRootMatch(matches);
    expect(rootMatch.params).toEqual({});
  });

  describe('that contains dynamic segments', function () {
    it('returns an array with the correct params', function () {
      var route = Route({ path: '/posts/:id/edit', handler: App });

      var matches = route.match('/posts/abc/edit');
      assert(matches);
      expect(matches.length).toEqual(1);

      var rootMatch = getRootMatch(matches);
      expect(rootMatch.params).toEqual({ id: 'abc' });
    });
  });
});

describe('a Route that does not match the URL', function () {
  it('returns null', function () {
    var route = Route({ path: '/a/b/c', handler: App });
    expect(route.match('/not-found')).toBe(null);
  });
});

describe('a nested Route that matches the URL', function () {
  it('returns the appropriate params for each match', function () {
    var route = Route({ name: 'posts', path: '/posts/:id', handler: App },
      Route({ name: 'comment', path: '/posts/:id/comments/:commentId', handler: App })
    );

    var matches = route.match('/posts/abc/comments/123');
    assert(matches);
    expect(matches.length).toEqual(2);

    var rootMatch = getRootMatch(matches);
    expect(rootMatch.route.props.name).toEqual('comment');
    expect(rootMatch.params).toEqual({ id: 'abc', commentId: '123' });

    var firstMatch = matches[0];
    expect(firstMatch.route.props.name).toEqual('posts');
    expect(firstMatch.params).toEqual({ id: 'abc' });
  });
});

describe('multiple nested Router that match the URL', function () {
  it('returns the first one in the subtree, depth-first', function () {
    var route = Route({ path: '/', handler: App },
      Route({ path: '/a', handler: App },
        Route({ path: '/a/b', name: 'expected', handler: App })
      ),
      Route({ path: '/a/b', handler: App })
    );

    var matches = route.match('/a/b');
    assert(matches);
    expect(matches.length).toEqual(3);

    var rootMatch = getRootMatch(matches);
    expect(rootMatch.route.props.name).toEqual('expected');
  });
});

describe('a Route with custom props', function() {
  it('receives props', function (done) {
    var route = Route({ handler: App, customProp: 'prop' });
    var component = TestUtils.renderIntoDocument(route);

    route.dispatch('/').then(function () {
      assert(component.props.customProp);
      expect(component.props.customProp).toEqual('prop');
      done();
    });
  });
});

describe('a Route', function() {
  it('requires a handler');
});

describe('a child route', function() {
  describe('path', function() {
    it('defaults to /');
    it('is not required to start with /');
    it('can be inferred from its name');
    it('must contain all dynamic segments of its parent route path');
  });

  describe('name', function() {
    it('cannot be reused');
  });
});

// describe('a Router that renders on the server', function() {
//   it('works with async willTransitionTo()', function(done) {
//     var dataStore = 'goodbye';
//     var Layout = React.createClass({
//       render: function() {
//         return React.DOM.article(null, this.props.activeRoute);
//       }
//     });
//     var AsyncApp = React.createClass({
//       displayName: 'AsyncApp',
//       statics: {
//         willTransitionTo: function() {
//           return new Promise(function(resolve) {
//             setTimeout(function() {
//               dataStore = 'hello';
//               resolve();
//             }, 0);
//           });
//         }
//       },
//       render: function() {
//         return React.DOM.div(null, dataStore);
//       }
//     });

//     var router = Router(
//       RouteComponent({ path: '/', handler: Layout},
//         RouteComponent({ path: '/a', handler: AsyncApp }))
//     );

//     router.renderComponentToString('/a').then(function(result) {
//       expect(result.indexOf('div') > -1).toBe(true);
//       expect(result.indexOf('hello') > -1).toBe(true);

//       done();
//     });
//   });
// });

function getRootMatch(matches) {
  return matches[matches.length - 1];
}
<|endoftext|>


<commit_before>// application menu
'use strict'

const {app, Menu} = require('electron')

const pkg = require('../package.json')

// file or application menu
const firstMenu = {
  label: 'File',
  submenu: [
    {role: 'quit'}
  ]
}

if (process.platform === 'darwin') {
  // if mac, first menu is application menu
  Object.assign(firstMenu, {
    label: app.getName(),
    submenu: [
      {role: 'about'},
      {type: 'separator'},
      {role: 'services', submenu: []},
      {type: 'separator'},
      {role: 'hide'},
      {role: 'hideothers'},
      {role: 'unhide'},
      {type: 'separator'}
    ].concat(firstMenu.submenu)
  })
}

const editMenu = {
  label: 'Edit',
  submenu: [
    {role: 'cut'},
    {role: 'copy'},
    {role: 'paste'},
    {role: 'selectall'}
  ]
}

const viewMenu = {
  label: 'View',
  submenu: [
    {role: 'reload'},
    {role: 'forcereload'},
    {type: 'separator'},
    {role: 'togglefullscreen'}
  ]
}

const windowMenu = {
  role: 'window',
  submenu: [
    {role: 'minimize'},
    {type: 'separator'},
    {role: 'front'}
  ]
}

const helpMenu = {
  role: 'help',
  submenu: [
    {
      label: 'Learn More',
      click: () => {
        require('electron').shell.openExternal('https://opentrons.com/')
      }
    },
    {
      label: 'Report an Issue',
      click: () => {
        require('electron').shell.openExternal(pkg.bugs.url)
      }
    }
  ]
}

const template = [firstMenu, editMenu, viewMenu, windowMenu, helpMenu]

module.exports = function initializeMenu () {
  Menu.setApplicationMenu(Menu.buildFromTemplate(template))
}
<commit_msg>fix(app-shell): Remove Reload from View menu unless devtools are active (#1659)<commit_after>// application menu
'use strict'

const {app, Menu} = require('electron')

const pkg = require('../package.json')
const config = require('./config').getConfig()

// file or application menu
const firstMenu = {
  label: 'File',
  submenu: [
    {role: 'quit'}
  ]
}

if (process.platform === 'darwin') {
  // if mac, first menu is application menu
  Object.assign(firstMenu, {
    label: app.getName(),
    submenu: [
      {role: 'about'},
      {type: 'separator'},
      {role: 'services', submenu: []},
      {type: 'separator'},
      {role: 'hide'},
      {role: 'hideothers'},
      {role: 'unhide'},
      {type: 'separator'},
      ...firstMenu.submenu
    ]
  })
}

const editMenu = {
  label: 'Edit',
  submenu: [
    {role: 'cut'},
    {role: 'copy'},
    {role: 'paste'},
    {role: 'selectall'}
  ]
}

const viewMenu = {
  label: 'View',
  submenu: [
    {role: 'togglefullscreen'}
  ]
}

if (config.devtools) {
  Object.assign(viewMenu, {
    submenu: [
      {role: 'reload'},
      {role: 'forcereload'},
      {type: 'separator'},
      ...viewMenu.submenu
    ]
  })
}

const windowMenu = {
  role: 'window',
  submenu: [
    {role: 'minimize'},
    {type: 'separator'},
    {role: 'front'}
  ]
}

const helpMenu = {
  role: 'help',
  submenu: [
    {
      label: 'Learn More',
      click: () => {
        require('electron').shell.openExternal('https://opentrons.com/')
      }
    },
    {
      label: 'Report an Issue',
      click: () => {
        require('electron').shell.openExternal(pkg.bugs.url)
      }
    }
  ]
}

const template = [firstMenu, editMenu, viewMenu, windowMenu, helpMenu]

module.exports = function initializeMenu () {
  Menu.setApplicationMenu(Menu.buildFromTemplate(template))
}
<|endoftext|>


<commit_before>'use strict';
const TwitterClient = require('twitter');

module.exports.hello = (event, context, callback) => {
  const Twitter = new TwitterClient({
    "consumer_key": process.env.CONSUMER_KEY,
    "consumer_secret": process.env.CONSUMER_SECRET,
    "access_token_key": process.env.TOKEN_KEY,
    "access_token_secret": process.env.TOKEN_SECRET
  });

  Twitter.post('statuses/update', { status: 'Hello world \#PeaceHack \#PeaceDay' }, function (error, tweet, response) {
    if (error) {
      console.log(error);
    }

    callback(null, {
      statusCode: 200,
      body: JSON.stringify({
        tweet: tweet,
        response: response
      }),
    });
  });
};
<commit_msg>Resolved conflict<commit_after>'use strict';
const TwitterClient = require('twitter');
const Tweets = require('./data/tweets.json')

const Twitter = new TwitterClient({
  "consumer_key": process.env.CONSUMER_KEY,
  "consumer_secret": process.env.CONSUMER_SECRET,
  "access_token_key": process.env.TOKEN_KEY,
  "access_token_secret": process.env.TOKEN_SECRET
});

module.exports.hello = (event, context, callback) => {
  Tweets.map((item) => {
    if (item.tweetOn === '') {
      Twitter.post('statuses/update', { status: item.message }, function (error, tweet, response) {
        if (error) {
          console.log(error);
        }

        callback(null, {
          statusCode: 200,
          body: JSON.stringify({
            tweet: tweet,
            response: response
          }),
        });
      });
    } else {
      console.log('Nothing to do');
    }
  });
};
<|endoftext|>


<commit_before>window.fix_div = function (div, height, right, fake_gen) {
    var div_offset = div.offset().top;
    if (right)
        var div_right = $(window).width() - div.offset().left - div.outerWidth();
    var is_moving;
    if (typeof fake_gen !== 'undefined')
        var fake = fake_gen(div);
    var moving = function () {
        div.css('position', 'absolute').css('top', div_offset);
        is_moving = true;
    };
    var fix = function () {
        div.css('position', 'fixed').css('top', height);
        is_moving = false;
    };
    if (right)
        div.css('right', div_right);
    if (typeof fake != 'undefined') {
        div.css('left', fake.offset().left).css('right', undefined);
        $(window).resize(function () {
            div.css('left', fake.offset().left);
        });
    }
    ($(window).scrollTop() - div_offset > -height) ? fix() : moving();
    $(window).scroll(function () {
        if (($(window).scrollTop() - div_offset > -height) == is_moving)
            is_moving ? fix() : moving();
    });
};

$(function () {
    fix_div($('#navigation'), 0, false, function (nav) {
        $('<div/>', {id: 'fake-nav'}).css('height', nav.height()).prependTo('#nav-head');
    });

    var $nav_list = $('#nav-list');
    $('#navicon').click(function (event) {
        event.stopPropagation();
        $nav_list.show('slow');
        $nav_list.click(function (event) {
            event.stopPropagation();
        });
        $('html').click(function () {
            $nav_list.hide('slow');
        });
    });
});
<commit_msg>@Xyene's phone too slow! #95<commit_after>window.fix_div = function (div, height, right, fake_gen) {
    var div_offset = div.offset().top;
    if (right)
        var div_right = $(window).width() - div.offset().left - div.outerWidth();
    var is_moving;
    if (typeof fake_gen !== 'undefined')
        var fake = fake_gen(div);
    var moving = function () {
        div.css('position', 'absolute').css('top', div_offset);
        is_moving = true;
    };
    var fix = function () {
        div.css('position', 'fixed').css('top', height);
        is_moving = false;
    };
    if (right)
        div.css('right', div_right);
    if (typeof fake != 'undefined') {
        div.css('left', fake.offset().left).css('right', undefined);
        $(window).resize(function () {
            div.css('left', fake.offset().left);
        });
    }
    ($(window).scrollTop() - div_offset > -height) ? fix() : moving();
    $(window).scroll(function () {
        if (($(window).scrollTop() - div_offset > -height) == is_moving)
            is_moving ? fix() : moving();
    });
};

$(function () {
    fix_div($('#navigation'), 0, false, function (nav) {
        $('<div/>', {id: 'fake-nav'}).css('height', nav.height()).prependTo('#nav-head');
    });

    var $nav_list = $('#nav-list');
    $('#navicon').click(function (event) {
        event.stopPropagation();
        $nav_list.show();
        $nav_list.click(function (event) {
            event.stopPropagation();
        });
        $('html').click(function () {
            $nav_list.hide();
        });
    });
});
<|endoftext|>


<commit_before>import chai from 'chai';

import * as GameTypes from '../../src/constants/gametypes';
import * as Helpers from '../../src/helpers/componenthelpers'

describe('Component Helpers', () => {
	describe('isInvalidInput()', () => {
		it('should return false for 3 <= x <= 10"', () => {
			const expectedResult = false;
			const actualResult = Helpers.isInvalidInput(5);
			chai.expect(actualResult).to.equal(expectedResult);
		});

		it('should return true for x < 3"', () => {
			const expectedResult = true;
			const actualResult = Helpers.isInvalidInput(2);
			chai.expect(actualResult).to.equal(expectedResult);
		});

		it('should return true for x > 10"', () => {
			const expectedResult = true;
			const actualResult = Helpers.isInvalidInput(12);
			chai.expect(actualResult).to.equal(expectedResult);
		});

		it('should return true for a NaN input"', () => {
			const expectedResult = true;
			const actualResult = Helpers.isInvalidInput(parseInt(""));
			chai.expect(actualResult).to.equal(expectedResult);
		});
	});
});<commit_msg>Add tests for getting endgame message.<commit_after>import chai from 'chai';

import * as GameTypes from '../../src/constants/gametypes';
import * as Helpers from '../../src/helpers/componenthelpers'

describe('Component Helpers', () => {
	describe('isInvalidInput()', () => {
		it('should return false for 3 <= x <= 10"', () => {
			const expectedResult = false;
			const actualResult = Helpers.isInvalidInput(5);
			chai.expect(actualResult).to.equal(expectedResult);
		});

		it('should return true for x < 3"', () => {
			const expectedResult = true;
			const actualResult = Helpers.isInvalidInput(2);
			chai.expect(actualResult).to.equal(expectedResult);
		});

		it('should return true for x > 10"', () => {
			const expectedResult = true;
			const actualResult = Helpers.isInvalidInput(12);
			chai.expect(actualResult).to.equal(expectedResult);
		});

		it('should return true for a NaN input"', () => {
			const expectedResult = true;
			const actualResult = Helpers.isInvalidInput(parseInt(""));
			chai.expect(actualResult).to.equal(expectedResult);
		});
	});

	describe('getEndGameMessage()', () => {
		it('should return "DRAW" given a GameType.DRAW"', () => {
			const expectedResult = GameTypes.DRAW;
			const actualResult = Helpers.getEndGameMessage(GameTypes.DRAW, GameTypes.PLAYER_ONE)
			chai.expect(actualResult).to.equal(expectedResult);
		});

		it('should return "GameTypes.PLAYER_TWO WINS!" given a non-draw GameType and GameTypes.PLAYER_ONE"', () => {
			const expectedResult = GameTypes.PLAYER_TWO + " WINS!";
			const actualResult = Helpers.getEndGameMessage(GameTypes.WINNER, GameTypes.PLAYER_ONE)
			chai.expect(actualResult).to.equal(expectedResult);
		});

		it('should return "GameTypes.PLAYER_ONE WINS!" given a non-draw GameType and GameTypes.PLAYER_TWO"', () => {
			const expectedResult = GameTypes.PLAYER_ONE + " WINS!";
			const actualResult = Helpers.getEndGameMessage(GameTypes.WINNER, GameTypes.PLAYER_TWO)
			chai.expect(actualResult).to.equal(expectedResult);
		});
	});
});<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var Tag           = require('../../../models/tag');
var TagHourlyStat = require('../../../models/tagHourlyStat');
var CheckEvent    = require('../../../models/checkEvent');
var async         = require('async');

/**
 * Check Routes
 */
module.exports = function(app) {

  app.get('/tags', function(req, res) {
    Tag
    .find()
    .sort({ name: 1 })
    .exec(function(err, tags) {
      if (err) return next(err);
      res.json(tags);
    });
  });

  // tag route middleware
  var loadTag = function(req, res, next) {
    Tag.findOne({ name: req.params.name }, function(err, tag) {
      if (err) return next(err);
      if (!tag) return res.json(404, { error: 'failed to load tag ' + req.params.name });
      req.tag = tag;
      next();
    });
  };

  app.get('/tags/:name', loadTag, function(req, res, next) {
    res.json(req.tag);
  });

  app.get('/tags/:name/checks/:period/:timestamp', loadTag, function(req, res, next) {
    req.tag.getChecksForPeriod(req.params.period, new Date(parseInt(req.params.timestamp)), function(err, checks) {
      if (err) return next(err);
      res.json(checks);
    })
  });

  app.get('/tags/:name/stat/:period/:timestamp', loadTag, function(req, res, next) {
    req.tag.getSingleStatsForPeriod(req.params.period, new Date(parseInt(req.params.timestamp)), function(err, stat) {
      if(err) return next(err);
      res.json(stat);
    });
  });

  app.get('/tags/:name/stats/:type', loadTag, function(req, res, next) {
    req.tag.getStatsForPeriod(req.params.type, req.query.begin, req.query.end, function(err, stats) {
      if(err) return next(err);
      res.json(stats);
    });
  });

  app.get('/tags/:name/events', function(req, res) {
    var query = {
      tags: req.params.name,
      timestamp: { $gte: req.query.begin || new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) }
    };
    if (req.query.end) {
      query.timestamp.$lte = req.query.end;
    }
    CheckEvent
    .find(query)
    .sort({ timestamp: -1 })
    .select({ tags: 0 })
    .limit(100)
    .exec(function(err, events) {
      if (err) return next(err);
      CheckEvent.aggregateEventsByDay(events, function(err, aggregatedEvents) {
        res.json(aggregatedEvents);
      });
    });
  });

};
<commit_msg>Tag delete api method<commit_after>/**
 * Module dependencies.
 */

var Check         = require('../../../models/check');
var Tag           = require('../../../models/tag');
var TagHourlyStat = require('../../../models/tagHourlyStat');
var CheckEvent    = require('../../../models/checkEvent');
var async         = require('async');

/**
 * Check Routes
 */
module.exports = function(app) {

  app.get('/tags', function(req, res) {
    Tag
    .find()
    .sort({ name: 1 })
    .exec(function(err, tags) {
      if (err) return next(err);
      res.json(tags);
    });
  });

  // tag route middleware
  var loadTag = function(req, res, next) {
    Tag.findOne({ name: req.params.name }, function(err, tag) {
      if (err) return next(err);
      if (!tag) return res.json(404, { error: 'failed to load tag ' + req.params.name });
      req.tag = tag;
      next();
    });
  };

  app.get('/tags/:name', loadTag, function(req, res, next) {
    res.json(req.tag);
  });

  app.get('/tags/:name/checks/:period/:timestamp', loadTag, function(req, res, next) {
    req.tag.getChecksForPeriod(req.params.period, new Date(parseInt(req.params.timestamp)), function(err, checks) {
      if (err) return next(err);
      res.json(checks);
    })
  });

  app.get('/tags/:name/stat/:period/:timestamp', loadTag, function(req, res, next) {
    req.tag.getSingleStatsForPeriod(req.params.period, new Date(parseInt(req.params.timestamp)), function(err, stat) {
      if(err) return next(err);
      res.json(stat);
    });
  });

  app.get('/tags/:name/stats/:type', loadTag, function(req, res, next) {
    req.tag.getStatsForPeriod(req.params.type, req.query.begin, req.query.end, function(err, stats) {
      if(err) return next(err);
      res.json(stats);
    });
  });

  app.get('/tags/:name/events', function(req, res) {
    var query = {
      tags: req.params.name,
      timestamp: { $gte: req.query.begin || new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) }
    };
    if (req.query.end) {
      query.timestamp.$lte = req.query.end;
    }
    CheckEvent
    .find(query)
    .sort({ timestamp: -1 })
    .select({ tags: 0 })
    .limit(100)
    .exec(function(err, events) {
      if (err) return next(err);
      CheckEvent.aggregateEventsByDay(events, function(err, aggregatedEvents) {
        res.json(aggregatedEvents);
      });
    });
  });

  app.delete('/tags/:name', loadTag, function(req, res, next) {
    // Delete tag relation first in order to avoid magic respawn
    Check.collection.update({ tags: req.tag.name }, { $pull: { tags: req.tag.name } }, { multi: true }, function(err) {
      if (err) return next(err);
      // Then, remove the tag
      req.tag.remove(function(err2) {
        if (err2) return next(err2);
        res.end();
      });
    })
  });

};
<|endoftext|>


<commit_before>// Copyright 2017 TODO Group. All rights reserved.
// Licensed under the Apache License, Version 2.0.

const linguist = require('../lib/linguist')
module.exports = function (targetDir) {
  let languages = []
  try {
    languages = linguist.identifyLanguagesSync(targetDir).map(language => language.toLowerCase())
  } catch (error) {
    console.log(`Linguist Axiom: Linguist not found in path, only running language-independent rules`)
  }
  return languages
}
<commit_msg>Better match to the json output from Linguist. Also improving error handling a little; either show the stack trace or indicate that it couldn't find Linguist on the command line<commit_after>// Copyright 2017 TODO Group. All rights reserved.
// Licensed under the Apache License, Version 2.0.

const linguist = require('../lib/linguist')
module.exports = function (targetDir) {
  let languages = []
  try {
    jsonObj = linguist.identifyLanguagesSync(targetDir)
    for(var language in jsonObj) {
      languages.push(language.toLowerCase())
    }
  } catch (error) {
    if(error.message === 'Linguist not installed') {
      console.log(`Linguist Axiom: Linguist not found in path, only running language-independent rules`)
    } else {
      console.log(error)
    }
  }
  return languages
}
<|endoftext|>


<commit_before>var jsonData;
var simpleJson = [];
var files = [];

function setUp()
{
    document.getElementById('fileinput').addEventListener('change', readFile, false);
    document.getElementById('jsonBtn').onclick = downloadJson;
    document.getElementById('txtBtn').onclick = toTxt;
    document.getElementById('csvBtn').onclick = toCsv;
    document.getElementById('htmlBtn').onclick = toHtml;
}

function readFile(evt)
{
    //Retrieve all the files from the FileList object
    document.getElementById("cannotParseAlert").className = "row hidden";
    document.getElementById("unknownMessageAlert").className = "row hidden";
    document.getElementById("parseBar").style.width = "0%";

    var files = evt.target.files;
    document.getElementById("fileNameTextBox").value = files[0].name;
    if (files)
    {
        var reader = new FileReader();
        reader.readAsText(files[0]);
        reader.onload = function()
        {
            try
            {
                jsonData = JSON.parse(reader.result);
                parseData();
            }
            catch (err)
            {
                console.warn(err);
                if (files[0].name === "Hangouts.json")
                {
                    if (err.__proto__.name === "SyntaxError")
                    {
                        document.getElementById("cannotParseAlertDes").innerText = "The Hangouts.json file seems to "+
                        "contain invalid JSON. Perhaps try download it from Google takeout again?";
                    }
                    else
                    {
                        document.getElementById("cannotParseAlertDes").innerHTML = "The Hangouts.json file contains data which "+
                        "cannot be read. If you could copy the error code:
 err.message
and report it "+
                        ""+
                        "here I will try fix it.";
                    }
                }
                else
                {
                    document.getElementById("cannotParseAlertDes").innerText = "Are you sure it was the Hangouts.json file you chose?";
                }
                document.getElementById("cannotParseAlert").className = "row";
            }
        }
    }
    else
    {
        console.error("Failed to load files");
    }
}

function parseData()
{
    var progress = 0;
    for (var i=0; i < jsonData.conversations.length; i++)
    {
        var conversation = {};
        conversation.chatName = "";
        conversation.participants = getParticipants(i);
        conversation.messages = [];

        for(var j=0; j < jsonData.conversations[i].events.length; j++)
        {
            var message = {};
            message.sender = {};
            message.sender.name = getName(jsonData.conversations[i].events[j].sender_id.gaia_id, conversation.participants);
            message.sender.id = jsonData.conversations[i].events[j].sender_id.gaia_id;
            message.unixtime = Math.floor(jsonData.conversations[i].events[j].timestamp/1000000);
            if(jsonData.conversations[i].events[j].chat_message !== undefined)
            {//if it's a message (normal hangouts, image...)
                if (jsonData.conversations[i].events[j].chat_message.message_content.segment !== undefined)
                {//if it's a normal hangouts message

                    var content ="";
                    for (var k = 0; k < jsonData.conversations[i].events[j].chat_message.message_content.segment.length; k++)
                    {
                        content += jsonData.conversations[i].events[j].chat_message.message_content.segment[k].text;
                    }
                    message.content = content;
                }

                else if (jsonData.conversations[i].events[j].chat_message.message_content.attachment !== undefined)
                {
                    for (var k = 0; k < jsonData.conversations[i].events[j].chat_message.message_content.attachment.length; k++)
                    {
                        //image
                        if (jsonData.conversations[i].events[j].chat_message.message_content.attachment[0].embed_item.type[0] == "PLUS_PHOTO")
                            message.content = jsonData.conversations[i].events[j].chat_message.message_content.attachment[k].embed_item.plus_photo.url
                        //audio
                        else if (jsonData.conversations[i].events[j].chat_message.message_content.attachment.type == "PLUS_AUDIO_V2")
                            message.content = jsonData.conversations[i].events[j].chat_message.message_content.attachment[k].embed_item["embeds.PlusAudioV2.plus_audio_v2"].url
                        else console.warn("Attachment detected that couldn't be parsed");
                    }
                }

                else
                {//if we don't recognise the format of the message
                    console.warn("%c Unknown format for conversation "+i+" message "+j+"", "background: #FF0000")
                    console.dir(jsonData.conversations[i].events[j]);
                    message.content = "Unknown format, unable to parse message "+j+" in conversation "+i;
                    document.getElementById("unknownMessageAlert").className = "row";
                }
            }
            else if (jsonData.conversations[i].events[j].conversation_rename)
            {//else if it's renaming the group
                message.content = "Changed group chat name to "+jsonData.conversations[i].events[j].conversation_rename.new_name;
            }
            else if (jsonData.conversations[i].events[j].hangout_event)
            {//else if it's a call using hangouts
                if (jsonData.conversations[i].events[j].hangout_event.event_type === "START_HANGOUT")
                {
                    message.content = "Started a Hangout";
                }
                else if (jsonData.conversations[i].events[j].hangout_event.event_type === "END_HANGOUT")
                {
                    message.content = "Ended a Hangout";
                }
            }
            else
            {//if it's not a message or renaming the group
               console.warn("%c Unknown format for conversation "+i+" message "+j+"", "background: #FF0000");
               console.dir(jsonData.conversations[i].events[j]);
               message.content = "Unknown format, unable to parse message "+j+" in conversation "+i;
               document.getElementById("unknownMessageAlert").className = "row";
            }
            conversation.messages.push(message);
            progress += ((1/jsonData.conversations.length)*(1/jsonData.conversations[i].events.length)*100);
            document.getElementById("parseBar").style.width = Math.floor(progress)+"%";
        }
        conversation.messages.sort(function(a, b)
        {
            return parseFloat(a.unixtime) - parseFloat(b.unixtime);
        });
        simpleJson.push(conversation);
        simpleJson[i].chatName = nameFile(i);
    }
    files = [];
    document.getElementById("jsonBtn").className = "btn btn-default colouredButton";
    document.getElementById("txtBtn").className = "btn btn-default colouredButton";
    document.getElementById("csvBtn").className = "btn btn-default colouredButton";
    document.getElementById("htmlBtn").className = "btn btn-default colouredButton";
    document.getElementById("parseBar").style.width = "100%";
}

function getParticipants(index)
{
    var participants = [];
    for (var i = 0; i < jsonData.conversations[index].conversation.conversation.participant_data.length; i++)
    {
        var person = {};
        person.id = jsonData.conversations[index].conversation.conversation.participant_data[i].id.gaia_id;
        if (jsonData.conversations[index].conversation.conversation.participant_data[i].fallback_name !== undefined)
        {
            person.name = jsonData.conversations[index].conversation.conversation.participant_data[i].fallback_name;
        }
        else
        {
            person.name = jsonData.conversations[index].conversation.conversation.participant_data[i].id.gaia_id;
        }
        participants.push(person);
    }
    return participants;
}

function getName(id, participants)
{
    for (var i = 0; i < participants.length; i++)
    {
        if(id === participants[i].id)
        {
            return participants[i].name;
        }
    }
    console.warn("Name not found for "+id+" in");
    console.dir(participants);
    return id;
}

function toTxt()
{
    files = [];
    document.getElementById("toFileBar").style.width = "0%";
    var progress = 0;
    for (var i=0; i < simpleJson.length; i++)
    {
        var conversation = {};
        conversation.type = ".txt";
        conversation.name = nameFile(i);
        conversation.messages = "";
        for (var j=0;j< simpleJson[i].messages.length; j++)
        {
            conversation.messages += simpleJson[i].messages[j].sender.name +" at "+unixToReadable(simpleJson[i].messages[j].unixtime)+
            " sent: "+simpleJson[i].messages[j].content+"\r\n";
            progress += (1/simpleJson.length)*(1/simpleJson[i].messages.length)*100;
            document.getElementById("toFileBar").style.width = Math.floor(progress)+"%";
        }
        files.push(conversation);
    }
    angular.element(document.getElementById('body')).scope().showFiles();
    document.getElementById("toFileBar").style.width = "100%";
}

function toCsv()
{
    files = [];
    document.getElementById("toFileBar").style.width = "0%";
    var progress = 0;
    for (var i=0; i < simpleJson.length; i++)
    {
        var conversation = {};
        conversation.type = ".csv";
        conversation.name = nameFile(i);
        conversation.messages = "";
        for (var j=0;j< simpleJson[i].messages.length; j++)
        {
            conversation.messages += simpleJson[i].messages[j].sender.name +","+unixToReadable(simpleJson[i].messages[j].unixtime)+
            ","+simpleJson[i].messages[j].content+"\r\n";
            progress += (1/simpleJson.length)*(1/simpleJson[i].messages.length)*100;
            document.getElementById("toFileBar").style.width = Math.floor(progress)+"%";
        }
        files.push(conversation);
    }
    angular.element(document.getElementById('body')).scope().showFiles();
    document.getElementById("toFileBar").style.width = "100%";
}

function toHtml()
{
    files = [];
    document.getElementById("toFileBar").style.width = "0%";
    var progress = 0;
    for (var i=0; i < simpleJson.length; i++)
    {
        var conversation = {};
        conversation.type = ".html";
        conversation.name = nameFile(i);
        conversation.messages = "<!DOCTYPE html><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'>"+
        "<title>Hangons Backup</title><link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>"+
        "";
        for (var j=0;j< simpleJson[i].messages.length; j++)
        {
            conversation.messages += getLetterCircle(i,simpleJson[i].messages[j].sender) +"
"+
            simpleJson[i].messages[j].content+"
"+simpleJson[i].messages[j].sender.name +", "
            +unixToReadable(simpleJson[i].messages[j].unixtime)+"

"+"\r\n

";

            progress += (1/simpleJson.length)*(1/simpleJson[i].messages.length)*100;
            document.getElementById("toFileBar").style.width = Math.floor(progress)+"%";
        }
        conversation.messages+="";
        files.push(conversation);
    }
    angular.element(document.getElementById('body')).scope().showFiles();
    document.getElementById("toFileBar").style.width = "100%";
}

function getLetterCircle(i, sender)
{
    if (sender.id === jsonData.conversations[i].conversation.conversation.self_conversation_state.self_read_state.participant_id.gaia_id)
    {
        return "";
    }
    return "
"+sender.name.charAt(0)+"
";
}

function getMessageClass(i, sender)
{
    if (sender.id === jsonData.conversations[i].conversation.conversation.self_conversation_state.self_read_state.participant_id.gaia_id)
    {
        return "s";
    }
    return "r";
}

function unixToReadable(unix)
{
    var d = new Date(0); //0 means it sets the date to the epoch
    d.setUTCSeconds(unix);
    return(d.toLocaleTimeString() +", "+ d.toDateString());
}

function nameFile(i)
{
    if ((jsonData.conversations[i].conversation.conversation.name !== undefined)&&
        (jsonData.conversations[i].conversation.conversation.name != ""))
    {
        return jsonData.conversations[i].conversation.conversation.name;
    }
    var participants = [];
    var index;
    for (var k=0; k < simpleJson[i].participants.length; k++)
    {
        participants[k] = simpleJson[i].participants[k].name;
        if (simpleJson[i].participants[k].id === jsonData.conversations[i].conversation.conversation.self_conversation_state.self_read_state.participant_id.gaia_id)
        {
            index = k;
        }
    }
    var client = participants.splice(index, 1);
    var name = participants.toString();
    participants.splice(index, 0, client);//Puts the person's name back into the array
    return name;
}

function downloadJson()
{
    document.getElementById("toFileBar").style.width = "0%";
    download("hangons.json", JSON.stringify(simpleJson, null, "\t"));
    document.getElementById("toFileBar").style.width = "100%";
}

function download(filename, text) {
    var a = window.document.createElement('a');
    a.href = window.URL.createObjectURL(new Blob([text], {type: 'text/plain'}));
    a.download = filename;

    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
}

var hangons = angular.module('hangons', []);
hangons.controller('mainController', function ($scope)
{
    $scope.showFiles=function()
    {
        $scope.angFiles = files;
        $scope.$apply();
    };

    $scope.angDownload=function(fileName, fileValue)
    {
        download(fileName, fileValue);
    }

    $scope.angDownloadAll=function()
    {
        for (const file of $scope.angFiles)
        {
            download(file.name + file.type, file.messages);
        }
    }

    $scope.testAngular=function()
    {
        alert("test Passed");
    }
});

window.onload = setUp;
<commit_msg>Handle ADD_USER and REMOVE_USER events (#11)<commit_after>var jsonData;
var simpleJson = [];
var files = [];

function setUp()
{
    document.getElementById('fileinput').addEventListener('change', readFile, false);
    document.getElementById('jsonBtn').onclick = downloadJson;
    document.getElementById('txtBtn').onclick = toTxt;
    document.getElementById('csvBtn').onclick = toCsv;
    document.getElementById('htmlBtn').onclick = toHtml;
}

function readFile(evt)
{
    //Retrieve all the files from the FileList object
    document.getElementById("cannotParseAlert").className = "row hidden";
    document.getElementById("unknownMessageAlert").className = "row hidden";
    document.getElementById("parseBar").style.width = "0%";

    var files = evt.target.files;
    document.getElementById("fileNameTextBox").value = files[0].name;
    if (files)
    {
        var reader = new FileReader();
        reader.readAsText(files[0]);
        reader.onload = function()
        {
            try
            {
                jsonData = JSON.parse(reader.result);
                parseData();
            }
            catch (err)
            {
                console.warn(err);
                if (files[0].name === "Hangouts.json")
                {
                    if (err.__proto__.name === "SyntaxError")
                    {
                        document.getElementById("cannotParseAlertDes").innerText = "The Hangouts.json file seems to "+
                        "contain invalid JSON. Perhaps try download it from Google takeout again?";
                    }
                    else
                    {
                        document.getElementById("cannotParseAlertDes").innerHTML = "The Hangouts.json file contains data which "+
                        "cannot be read. If you could copy the error code:
 err.message
and report it "+
                        ""+
                        "here I will try fix it.";
                    }
                }
                else
                {
                    document.getElementById("cannotParseAlertDes").innerText = "Are you sure it was the Hangouts.json file you chose?";
                }
                document.getElementById("cannotParseAlert").className = "row";
            }
        }
    }
    else
    {
        console.error("Failed to load files");
    }
}

function parseData()
{
    var progress = 0;
    for (var i=0; i < jsonData.conversations.length; i++)
    {
        var conversation = {};
        conversation.chatName = "";
        conversation.participants = getParticipants(i);
        conversation.messages = [];

        for(var j=0; j < jsonData.conversations[i].events.length; j++)
        {
            var message = {};
            message.sender = {};
            message.sender.name = getName(jsonData.conversations[i].events[j].sender_id.gaia_id, conversation.participants);
            message.sender.id = jsonData.conversations[i].events[j].sender_id.gaia_id;
            message.unixtime = Math.floor(jsonData.conversations[i].events[j].timestamp/1000000);
            if(jsonData.conversations[i].events[j].chat_message !== undefined)
            {//if it's a message (normal hangouts, image...)
                if (jsonData.conversations[i].events[j].chat_message.message_content.segment !== undefined)
                {//if it's a normal hangouts message

                    var content ="";
                    for (var k = 0; k < jsonData.conversations[i].events[j].chat_message.message_content.segment.length; k++)
                    {
                        content += jsonData.conversations[i].events[j].chat_message.message_content.segment[k].text;
                    }
                    message.content = content;
                }

                else if (jsonData.conversations[i].events[j].chat_message.message_content.attachment !== undefined)
                {
                    for (var k = 0; k < jsonData.conversations[i].events[j].chat_message.message_content.attachment.length; k++)
                    {
                        //image
                        if (jsonData.conversations[i].events[j].chat_message.message_content.attachment[0].embed_item.type[0] == "PLUS_PHOTO")
                            message.content = jsonData.conversations[i].events[j].chat_message.message_content.attachment[k].embed_item.plus_photo.url
                        //audio
                        else if (jsonData.conversations[i].events[j].chat_message.message_content.attachment.type == "PLUS_AUDIO_V2")
                            message.content = jsonData.conversations[i].events[j].chat_message.message_content.attachment[k].embed_item["embeds.PlusAudioV2.plus_audio_v2"].url
                        else console.warn("Attachment detected that couldn't be parsed");
                    }
                }

                else
                {//if we don't recognise the format of the message
                    console.warn("%c Unknown format for conversation "+i+" message "+j+"", "background: #FF0000")
                    console.dir(jsonData.conversations[i].events[j]);
                    message.content = "Unknown format, unable to parse message "+j+" in conversation "+i;
                    document.getElementById("unknownMessageAlert").className = "row";
                }
            }
            else if (jsonData.conversations[i].events[j].conversation_rename)
            {//else if it's renaming the group
                message.content = "Changed group chat name to "+jsonData.conversations[i].events[j].conversation_rename.new_name;
            }
            else if (jsonData.conversations[i].events[j].hangout_event)
            {//else if it's a call using hangouts
                if (jsonData.conversations[i].events[j].hangout_event.event_type === "START_HANGOUT")
                {
                    message.content = "Started a Hangout";
                }
                else if (jsonData.conversations[i].events[j].hangout_event.event_type === "END_HANGOUT")
                {
                    message.content = "Ended a Hangout";
                }
            }
            else if (jsonData.conversations[i].events[j].event_type === "ADD_USER")
            {//else if it's a new user joining
                message.content = message.sender.name+" has joined the hangout"
            }//else if it's a user leaving
            else if (jsonData.conversations[i].events[j].event_type === "REMOVE_USER")
            {
                message.content = message.sender.name+" has left the hangout"
            }
            else
            {//if it's not a message or renaming the group
               console.warn("%c Unknown format for conversation "+i+" message "+j+"", "background: #FF0000");
               console.dir(jsonData.conversations[i].events[j]);
               message.content = "Unknown format, unable to parse message "+j+" in conversation "+i;
               document.getElementById("unknownMessageAlert").className = "row";
            }
            conversation.messages.push(message);
            progress += ((1/jsonData.conversations.length)*(1/jsonData.conversations[i].events.length)*100);
            document.getElementById("parseBar").style.width = Math.floor(progress)+"%";
        }
        conversation.messages.sort(function(a, b)
        {
            return parseFloat(a.unixtime) - parseFloat(b.unixtime);
        });
        simpleJson.push(conversation);
        simpleJson[i].chatName = nameFile(i);
    }
    files = [];
    document.getElementById("jsonBtn").className = "btn btn-default colouredButton";
    document.getElementById("txtBtn").className = "btn btn-default colouredButton";
    document.getElementById("csvBtn").className = "btn btn-default colouredButton";
    document.getElementById("htmlBtn").className = "btn btn-default colouredButton";
    document.getElementById("parseBar").style.width = "100%";
}

function getParticipants(index)
{
    var participants = [];
    for (var i = 0; i < jsonData.conversations[index].conversation.conversation.participant_data.length; i++)
    {
        var person = {};
        person.id = jsonData.conversations[index].conversation.conversation.participant_data[i].id.gaia_id;
        if (jsonData.conversations[index].conversation.conversation.participant_data[i].fallback_name !== undefined)
        {
            person.name = jsonData.conversations[index].conversation.conversation.participant_data[i].fallback_name;
        }
        else
        {
            person.name = jsonData.conversations[index].conversation.conversation.participant_data[i].id.gaia_id;
        }
        participants.push(person);
    }
    return participants;
}

function getName(id, participants)
{
    for (var i = 0; i < participants.length; i++)
    {
        if(id === participants[i].id)
        {
            return participants[i].name;
        }
    }
    console.warn("Name not found for "+id+" in");
    console.dir(participants);
    return id;
}

function toTxt()
{
    files = [];
    document.getElementById("toFileBar").style.width = "0%";
    var progress = 0;
    for (var i=0; i < simpleJson.length; i++)
    {
        var conversation = {};
        conversation.type = ".txt";
        conversation.name = nameFile(i);
        conversation.messages = "";
        for (var j=0;j< simpleJson[i].messages.length; j++)
        {
            conversation.messages += simpleJson[i].messages[j].sender.name +" at "+unixToReadable(simpleJson[i].messages[j].unixtime)+
            " sent: "+simpleJson[i].messages[j].content+"\r\n";
            progress += (1/simpleJson.length)*(1/simpleJson[i].messages.length)*100;
            document.getElementById("toFileBar").style.width = Math.floor(progress)+"%";
        }
        files.push(conversation);
    }
    angular.element(document.getElementById('body')).scope().showFiles();
    document.getElementById("toFileBar").style.width = "100%";
}

function toCsv()
{
    files = [];
    document.getElementById("toFileBar").style.width = "0%";
    var progress = 0;
    for (var i=0; i < simpleJson.length; i++)
    {
        var conversation = {};
        conversation.type = ".csv";
        conversation.name = nameFile(i);
        conversation.messages = "";
        for (var j=0;j< simpleJson[i].messages.length; j++)
        {
            conversation.messages += simpleJson[i].messages[j].sender.name +","+unixToReadable(simpleJson[i].messages[j].unixtime)+
            ","+simpleJson[i].messages[j].content+"\r\n";
            progress += (1/simpleJson.length)*(1/simpleJson[i].messages.length)*100;
            document.getElementById("toFileBar").style.width = Math.floor(progress)+"%";
        }
        files.push(conversation);
    }
    angular.element(document.getElementById('body')).scope().showFiles();
    document.getElementById("toFileBar").style.width = "100%";
}

function toHtml()
{
    files = [];
    document.getElementById("toFileBar").style.width = "0%";
    var progress = 0;
    for (var i=0; i < simpleJson.length; i++)
    {
        var conversation = {};
        conversation.type = ".html";
        conversation.name = nameFile(i);
        conversation.messages = "<!DOCTYPE html><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'>"+
        "<title>Hangons Backup</title><link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>"+
        "";
        for (var j=0;j< simpleJson[i].messages.length; j++)
        {
            conversation.messages += getLetterCircle(i,simpleJson[i].messages[j].sender) +"
"+
            simpleJson[i].messages[j].content+"
"+simpleJson[i].messages[j].sender.name +", "
            +unixToReadable(simpleJson[i].messages[j].unixtime)+"

"+"\r\n

";

            progress += (1/simpleJson.length)*(1/simpleJson[i].messages.length)*100;
            document.getElementById("toFileBar").style.width = Math.floor(progress)+"%";
        }
        conversation.messages+="";
        files.push(conversation);
    }
    angular.element(document.getElementById('body')).scope().showFiles();
    document.getElementById("toFileBar").style.width = "100%";
}

function getLetterCircle(i, sender)
{
    if (sender.id === jsonData.conversations[i].conversation.conversation.self_conversation_state.self_read_state.participant_id.gaia_id)
    {
        return "";
    }
    return "
"+sender.name.charAt(0)+"
";
}

function getMessageClass(i, sender)
{
    if (sender.id === jsonData.conversations[i].conversation.conversation.self_conversation_state.self_read_state.participant_id.gaia_id)
    {
        return "s";
    }
    return "r";
}

function unixToReadable(unix)
{
    var d = new Date(0); //0 means it sets the date to the epoch
    d.setUTCSeconds(unix);
    return(d.toLocaleTimeString() +", "+ d.toDateString());
}

function nameFile(i)
{
    if ((jsonData.conversations[i].conversation.conversation.name !== undefined)&&
        (jsonData.conversations[i].conversation.conversation.name != ""))
    {
        return jsonData.conversations[i].conversation.conversation.name;
    }
    var participants = [];
    var index;
    for (var k=0; k < simpleJson[i].participants.length; k++)
    {
        participants[k] = simpleJson[i].participants[k].name;
        if (simpleJson[i].participants[k].id === jsonData.conversations[i].conversation.conversation.self_conversation_state.self_read_state.participant_id.gaia_id)
        {
            index = k;
        }
    }
    var client = participants.splice(index, 1);
    var name = participants.toString();
    participants.splice(index, 0, client);//Puts the person's name back into the array
    return name;
}

function downloadJson()
{
    document.getElementById("toFileBar").style.width = "0%";
    download("hangons.json", JSON.stringify(simpleJson, null, "\t"));
    document.getElementById("toFileBar").style.width = "100%";
}

function download(filename, text) {
    var a = window.document.createElement('a');
    a.href = window.URL.createObjectURL(new Blob([text], {type: 'text/plain'}));
    a.download = filename;

    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
}

var hangons = angular.module('hangons', []);
hangons.controller('mainController', function ($scope)
{
    $scope.showFiles=function()
    {
        $scope.angFiles = files;
        $scope.$apply();
    };

    $scope.angDownload=function(fileName, fileValue)
    {
        download(fileName, fileValue);
    }

    $scope.angDownloadAll=function()
    {
        for (const file of $scope.angFiles)
        {
            download(file.name + file.type, file.messages);
        }
    }

    $scope.testAngular=function()
    {
        alert("test Passed");
    }
});

window.onload = setUp;
<|endoftext|>


<commit_before>/**
 *
 * Find more about the original version of Spinning Wheel function go to
 * http://cubiq.org/spinning-wheel-on-webkit-for-iphone-ipod-touch/11
 *
 * Copyright (c) 2016 Aleksandr Shevelev, http://alexalv.github.io/
 * Released under MIT license
 *
 *
 * Version 2.0alpha
 *
 */

function SpinningWheel(data, options){

    var self = this;
    this.slotData = [];

    this.defaultOptions = {
        cellHeight: 44,
        friction: 0.003,
        mainElementId: 'sw-wrapper'
    };

    self.options = {};

    this.init = function() {
        console.log("CALL INIT");

        for (var optionName in self.defaultOptions) {
            if (!options[optionName]) {
                self.options[optionName] = self.defaultOptions[optionName];
            } else {
                self.options[optionName] = options[optionName];
            }
        }

        self.reset();

        data.forEach(function(slotData){
            self.addSlot(slotData['data'],slotData['position'],slotData['defaultValue']);
        });

        self.create();
    };

    /**
     *
     * Event handler
     *
     */

    this.handleEvent = function (e) {
        if (e.type == 'touchstart') {
            this.lockScreen(e);
            if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
                this.tapDown(e);
            } else if (e.currentTarget.id == 'sw-frame') {
                this.scrollStart(e);
            }
        } else if (e.type == 'touchmove') {
            this.lockScreen(e);

            if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
                this.tapCancel(e);
            } else if (e.currentTarget.id == 'sw-frame') {
                this.scrollMove(e);
            }
        } else if (e.type == 'touchend') {
            if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
                this.tapUp(e);
            } else if (e.currentTarget.id == 'sw-frame') {
                this.scrollEnd(e);
            }
        } else if (e.type == 'webkitTransitionEnd') {
            if (e.target.id == 'sw-wrapper') {
                this.destroy();
            } else {
                this.backWithinBoundaries(e);
            }
        } else if (e.type == 'orientationchange') {
            this.onOrientationChange(e);
        } else if (e.type == 'scroll') {
            this.onScroll(e);
        }
    };

    /**
     *
     * Global events
     *
     */

    this.onOrientationChange = function (e) {
        window.scrollTo(0, 0);
        //this.swWrapper.style.top = window.innerHeight + window.pageYOffset + 'px';
        this.calculateSlotsWidth();
    };

    this.onScroll = function (e) {
        //this.swWrapper.style.top = window.innerHeight + window.pageYOffset + 'px';
    };

    this.lockScreen = function (e) {
        e.preventDefault();
        e.stopPropagation();
    };

    /**
     *
     * Initialization
     *
     */

    this.reset = function () {
        this.slotEl = [];

        this.activeSlot = null;

        this.swWrapper = undefined;
        this.swSlotWrapper = undefined;
        this.swSlots = undefined;
        this.swFrame = undefined;
    };

    this.calculateSlotsWidth = function () {
        var div = this.swSlots.getElementsByTagName('div');
        for (var i = 0; i < div.length; i += 1) {
            this.slotEl[i].slotWidth = div[i].offsetWidth;
        }
    };

    this.create = function () {
        var i, l, out, ul, div;
        this.reset();   // Initialize object variables
        // Find the Spinning Wheel main wrapper
        div = document.getElementById(this.options.mainElementId);
        //div.style.top = window.innerHeight + window.pageYOffset + 'px';     // Place the SW down the actual viewing screen
        div.style.webkitTransitionProperty = '-webkit-transform';
        div.innerHTML = '
'
                        +    '
'
                        +    '
'
                        +'
'
                        +'
'
                        +'
';

        document.body.appendChild(div);

        this.swWrapper = div; // The SW wrapper
        console.log(div.childNodes);
        this.swSlotWrapper = div.childNodes[0];       // Slots visible area
        this.swSlots = this.swSlotWrapper.childNodes[0];                     // Pseudo table element (inner wrapper)
        this.swFrame = div.childNodes[1];                     // The scrolling controller

        // Create HTML slot elements
        for (l = 0; l < this.slotData.length; l += 1) {
            // Create the slot
            ul = document.createElement('ul');
            out = '';
            for (i in this.slotData[l].values) {
                out += '
' + this.slotData[l].values[i] + '<' + '/li>';
            }
            ul.innerHTML = out;

            div = document.createElement('div');        // Create slot container
            div.className = this.slotData[l].style;     // Add styles to the container
            div.appendChild(ul);

            // Append the slot to the wrapper
            this.swSlots.appendChild(div);

            ul.slotPosition = l;            // Save the slot position inside the wrapper
            ul.slotYPosition = 0;
            ul.slotWidth = 0;
            ul.slotMaxScroll = this.swSlotWrapper.clientHeight - ul.clientHeight - 86;
            ul.style.webkitTransitionTimingFunction = 'cubic-bezier(0, 0, 0.2, 1)';     // Add default transition

            this.slotEl.push(ul);           // Save the slot for later use

            // Place the slot to its default position (if other than 0)
            if (this.slotData[l].defaultValue) {
                this.scrollToValue(l, this.slotData[l].defaultValue);
            }
        }

        this.calculateSlotsWidth();

        // Global events
        document.addEventListener('touchstart', this, false);           // Prevent page scrolling
        document.addEventListener('touchmove', this, false);            // Prevent page scrolling
        window.addEventListener('orientationchange', this, true);       // Optimize SW on orientation change
        window.addEventListener('scroll', this, true);              // Reposition SW on page scroll

        // Add scrolling to the slots
        this.swFrame.addEventListener('touchstart', this, false);
    };

    /**
     *
     * Generic methods
     *
     */

    this.addSlot = function (values, style, defaultValue) {
        if (!style) {
            style = '';
        }

        style = style.split(' ');

        for (var i = 0; i < style.length; i += 1) {
            style[i] = 'sw-' + style[i];
        }

        style = style.join(' ');

        var obj = { 'values': values, 'style': style, 'defaultValue': defaultValue };
        this.slotData.push(obj);
    };

    this.getSelectedValues = function () {
        var index, count,
            i, l,
            keys = [], values = [];

        for (i in this.slotEl) {
            // Remove any residual animation
            this.slotEl[i].removeEventListener('webkitTransitionEnd', this, false);
            this.slotEl[i].style.webkitTransitionDuration = '0';

            if (this.slotEl[i].slotYPosition > 0) {
                this.setPosition(i, 0);
            } else if (this.slotEl[i].slotYPosition < this.slotEl[i].slotMaxScroll) {
                this.setPosition(i, this.slotEl[i].slotMaxScroll);
            }

            index = -Math.round(this.slotEl[i].slotYPosition / this.options.cellHeight);

            count = 0;
            for (l in this.slotData[i].values) {
                if (count == index) {
                    keys.push(l);
                    values.push(this.slotData[i].values[l]);
                    break;
                }

                count += 1;
            }
        }

        return { 'keys': keys, 'values': values };
    };

    /**
     *
     * Rolling slots
     *
     */

    this.setPosition = function (slot, pos) {
        this.slotEl[slot].slotYPosition = pos;
        this.slotEl[slot].style.webkitTransform = 'translate3d(0, ' + pos + 'px, 0)';
    };

    this.scrollStart = function (e) {
        // Find the clicked slot
        var xPos = e.targetTouches[0].clientX - this.swSlots.offsetLeft;    // Clicked position minus left offset (should be 11px)

        // Find tapped slot
        var slot = 0;
        for (var i = 0; i < this.slotEl.length; i += 1) {
            slot += this.slotEl[i].slotWidth;

            if (xPos < slot) {
                this.activeSlot = i;
                break;
            }
        }

        // If slot is readonly do nothing
        if (this.slotData[this.activeSlot].style.match('readonly')) {
            this.swFrame.removeEventListener('touchmove', this, false);
            this.swFrame.removeEventListener('touchend', this, false);
            return false;
        }

        this.slotEl[this.activeSlot].removeEventListener('webkitTransitionEnd', this, false);   // Remove transition event (if any)
        this.slotEl[this.activeSlot].style.webkitTransitionDuration = '0';      // Remove any residual transition

        // Stop and hold slot position
        var theTransform = window.getComputedStyle(this.slotEl[this.activeSlot]).webkitTransform;
        theTransform = new WebKitCSSMatrix(theTransform).m42;
        if (theTransform != this.slotEl[this.activeSlot].slotYPosition) {
            this.setPosition(this.activeSlot, theTransform);
        }

        this.startY = e.targetTouches[0].clientY;
        this.scrollStartY = this.slotEl[this.activeSlot].slotYPosition;
        this.scrollStartTime = e.timeStamp;

        this.swFrame.addEventListener('touchmove', this, false);
        this.swFrame.addEventListener('touchend', this, false);

        return true;
    };

    this.scrollMove = function (e) {
        var topDelta = e.targetTouches[0].clientY - this.startY;

        if (this.slotEl[this.activeSlot].slotYPosition > 0 || this.slotEl[this.activeSlot].slotYPosition < this.slotEl[this.activeSlot].slotMaxScroll) {
            topDelta /= 2;
        }

        this.setPosition(this.activeSlot, this.slotEl[this.activeSlot].slotYPosition + topDelta);
        this.startY = e.targetTouches[0].clientY;

        // Prevent slingshot effect
        if (e.timeStamp - this.scrollStartTime > 80) {
            this.scrollStartY = this.slotEl[this.activeSlot].slotYPosition;
            this.scrollStartTime = e.timeStamp;
        }
        //this.getElementById('acslot').innerHTML = 'moving';
        //this.getElementById('newp').innerHTML = 'moving';
    };

    this.scrollEnd = function (e) {
        this.swFrame.removeEventListener('touchmove', this, false);
        this.swFrame.removeEventListener('touchend', this, false);

        // If we are outside of the boundaries, let's go back to the sheepfold
        if (this.slotEl[this.activeSlot].slotYPosition > 0 || this.slotEl[this.activeSlot].slotYPosition < this.slotEl[this.activeSlot].slotMaxScroll) {
            this.scrollTo(this.activeSlot, this.slotEl[this.activeSlot].slotYPosition > 0 ? 0 : this.slotEl[this.activeSlot].slotMaxScroll);
            return false;
        }

        // Lame formula to calculate a fake deceleration
        var scrollDistance = this.slotEl[this.activeSlot].slotYPosition - this.scrollStartY;

        // The drag session was too short
        if (scrollDistance < this.options.cellHeight / 1.5 && scrollDistance > -this.options.cellHeight / 1.5) {
            if (this.slotEl[this.activeSlot].slotYPosition % this.options.cellHeight) {
                this.scrollTo(this.activeSlot, Math.round(this.slotEl[this.activeSlot].slotYPosition / this.options.cellHeight) * this.options.cellHeight, '100ms');
            }

            return false;
        }

        var scrollDuration = e.timeStamp - this.scrollStartTime;

        var newDuration = (2 * scrollDistance / scrollDuration) / this.options.friction;
        var newScrollDistance = (this.options.friction / 2) * (newDuration * newDuration);

        if (newDuration < 0) {
            newDuration = -newDuration;
            newScrollDistance = -newScrollDistance;
        }

        var newPosition = this.slotEl[this.activeSlot].slotYPosition + newScrollDistance;

        if (newPosition > 0) {
            // Prevent the slot to be dragged outside the visible area (top margin)
            newPosition /= 2;
            newDuration /= 3;

            if (newPosition > this.swSlotWrapper.clientHeight / 4) {
                newPosition = this.swSlotWrapper.clientHeight / 4;
            }
        } else if (newPosition < this.slotEl[this.activeSlot].slotMaxScroll) {
            // Prevent the slot to be dragged outside the visible area (bottom margin)
            newPosition = (newPosition - this.slotEl[this.activeSlot].slotMaxScroll) / 2 + this.slotEl[this.activeSlot].slotMaxScroll;
            newDuration /= 3;

            if (newPosition < this.slotEl[this.activeSlot].slotMaxScroll - this.swSlotWrapper.clientHeight / 4) {
                newPosition = this.slotEl[this.activeSlot].slotMaxScroll - this.swSlotWrapper.clientHeight / 4;
            }
        } else {
            newPosition = Math.round(newPosition / this.options.cellHeight) * this.options.cellHeight;
        }

        this.scrollTo(this.activeSlot, Math.round(newPosition), Math.round(newDuration) + 'ms');
        return true;
    };

    this.scrollTo = function (slotNum, dest, runtime) {
        this.slotEl[slotNum].style.webkitTransitionDuration = runtime ? runtime : '100ms';
        this.setPosition(slotNum, dest ? dest : 0);

        // If we are outside of the boundaries go back to the sheepfold
        if (this.slotEl[slotNum].slotYPosition > 0 || this.slotEl[slotNum].slotYPosition < this.slotEl[slotNum].slotMaxScroll) {
            this.slotEl[slotNum].addEventListener('webkitTransitionEnd', this, false);
        }
    };

    this.scrollToValue = function (slot, value) {
        var yPos, count, i;

        this.slotEl[slot].removeEventListener('webkitTransitionEnd', this, false);
        this.slotEl[slot].style.webkitTransitionDuration = '0';

        count = 0;
        for (i in this.slotData[slot].values) {
            if (i == value) {
                yPos = count * this.options.cellHeight;
                this.setPosition(slot, yPos);
                break;
            }

            count -= 1;
        }
    };

    this.backWithinBoundaries = function (e) {
        e.target.removeEventListener('webkitTransitionEnd', this, false);

        this.scrollTo(e.target.slotPosition, e.target.slotYPosition > 0 ? 0 : e.target.slotMaxScroll, '150ms');
        return false;
    }

    this.init();

}<commit_msg>js: allow more than one spinning wheel element on page with correct scroll<commit_after>/**
 *
 * Find more about the original version of Spinning Wheel function go to
 * http://cubiq.org/spinning-wheel-on-webkit-for-iphone-ipod-touch/11
 *
 * Copyright (c) 2016 Aleksandr Shevelev, http://alexalv.github.io/
 * Released under MIT license
 *
 * Version 2.0alpha
 *
 */

function SpinningWheel(data, options){

    var self = this;
    this.slotData = [];

    this.defaultOptions = {
        cellHeight: 44,
        friction: 0.003,
        mainElementId: 'sw-wrapper'
    };

    self.options = {};

    this.init = function() {

        for (var optionName in self.defaultOptions) {
            if (!options[optionName]) {
                self.options[optionName] = self.defaultOptions[optionName];
            } else {
                self.options[optionName] = options[optionName];
            }
        }

        self.reset();

        data.forEach(function(slotData){
            self.addSlot(slotData['data'],slotData['position'],slotData['defaultValue']);
        });

        self.create();
    };

    /**
     *
     * Event handler
     *
     */

    this.handleEvent = function (e) {
        if (e.type == 'touchstart') {
            self.lockScreen(e);
            if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
                self.tapDown(e);
            } else if (e.currentTarget.id == 'sw-frame') {
                self.scrollStart(e);
            }
        } else if (e.type == 'touchmove') {
            self.lockScreen(e);
            if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
                self.tapCancel(e);
            } else if (e.currentTarget.id == 'sw-frame') {
                self.scrollMove(e);
            }
        } else if (e.type == 'touchend') {
            if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
                self.tapUp(e);
            } else if (e.currentTarget.id == 'sw-frame') {
                self.scrollEnd(e);
            }
        } else if (e.type == 'webkitTransitionEnd') {
            if (e.target.id == 'sw-wrapper') {
                self.destroy();
            } else {
                self.backWithinBoundaries(e);
            }
        } else if (e.type == 'orientationchange') {
            self.onOrientationChange(e);
        } else if (e.type == 'scroll') {
            self.onScroll(e);
        }
    };

    /**
     *
     * Global events
     *
     */

    this.onOrientationChange = function (e) {
        window.scrollTo(0, 0);
        //this.swWrapper.style.top = window.innerHeight + window.pageYOffset + 'px';
        this.calculateSlotsWidth();
    };

    this.onScroll = function (e) {
        //this.swWrapper.style.top = window.innerHeight + window.pageYOffset + 'px';
    };

    this.lockScreen = function (e) {
        e.preventDefault();
        e.stopPropagation();
    };

    /**
     *
     * Initialization
     *
     */

    this.reset = function () {
        self.slotEl = [];

        self.activeSlot = null;

        self.swWrapper = undefined;
        self.swSlotWrapper = undefined;
        self.swSlots = undefined;
        self.swFrame = undefined;
    };

    this.calculateSlotsWidth = function () {
        var div = self.swSlots.getElementsByTagName('div');
        for (var i = 0; i < div.length; i += 1) {
            self.slotEl[i].slotWidth = div[i].offsetWidth;
        }
    };

    this.create = function () {
        var i, l, out, ul, div;
        self.reset();   // Initialize object variables
        // Find the Spinning Wheel main wrapper
        div = document.getElementById(self.options.mainElementId);
        //div.style.top = window.innerHeight + window.pageYOffset + 'px';     // Place the SW down the actual viewing screen
        //div.style.webkitTransitionProperty = '-webkit-transform';
        div.innerHTML = '
'
                        +    '
'
                        +    '
'
                        +'
'
                        +'
'
                        +'
';

        //document.body.appendChild(div);

        self.swWrapper = div; // The SW wrapper
        self.swSlotWrapper = div.childNodes[0];       // Slots visible area
        self.swSlots = self.swSlotWrapper.childNodes[0];                     // Pseudo table element (inner wrapper)
        self.swFrame = div.childNodes[1];                     // The scrolling controller

        // Create HTML slot elements
        for (l = 0; l < self.slotData.length; l += 1) {
            // Create the slot
            ul = document.createElement('ul');
            out = '';
            for (i in self.slotData[l].values) {
                out += '
' + self.slotData[l].values[i] + '<' + '/li>';
            }
            ul.innerHTML = out;

            div = document.createElement('div');        // Create slot container
            div.className = self.slotData[l].style;     // Add styles to the container
            div.appendChild(ul);

            // Append the slot to the wrapper
            self.swSlots.appendChild(div);

            ul.slotPosition = l;            // Save the slot position inside the wrapper
            ul.slotYPosition = 0;
            ul.slotWidth = 0;
            ul.slotMaxScroll = self.swSlotWrapper.clientHeight - ul.clientHeight - 86;
            ul.style.webkitTransitionTimingFunction = 'cubic-bezier(0, 0, 0.2, 1)';     // Add default transition

            self.slotEl.push(ul);           // Save the slot for later use

            // Place the slot to its default position (if other than 0)
            if (self.slotData[l].defaultValue) {
                self.scrollToValue(l, self.slotData[l].defaultValue);
            }
        }

        self.calculateSlotsWidth();

        // Global events
        //document.addEventListener('touchstart', self, false);           // Prevent page scrolling
        //document.addEventListener('touchmove', self, false);            // Prevent page scrolling
        //window.addEventListener('orientationchange', self, true);       // Optimize SW on orientation change
        //window.addEventListener('scroll', self, true);              // Reposition SW on page scroll

        // Add scrolling to the slots
        self.swFrame.addEventListener('touchstart', self, false);
    };

    /**
     *
     * Generic methods
     *
     */

    this.addSlot = function (values, style, defaultValue) {
        if (!style) {
            style = '';
        }

        style = style.split(' ');

        for (var i = 0; i < style.length; i += 1) {
            style[i] = 'sw-' + style[i];
        }

        style = style.join(' ');

        var obj = { 'values': values, 'style': style, 'defaultValue': defaultValue };
        self.slotData.push(obj);
    };

    this.getSelectedValues = function () {
        var index, count,
            i, l,
            keys = [], values = [];

        for (i in self.slotEl) {
            // Remove any residual animation
            self.slotEl[i].removeEventListener('webkitTransitionEnd', self, false);
            self.slotEl[i].style.webkitTransitionDuration = '0';

            if (self.slotEl[i].slotYPosition > 0) {
                self.setPosition(i, 0);
            } else if (self.slotEl[i].slotYPosition < self.slotEl[i].slotMaxScroll) {
                self.setPosition(i, self.slotEl[i].slotMaxScroll);
            }

            index = -Math.round(self.slotEl[i].slotYPosition / self.options.cellHeight);

            count = 0;
            for (l in self.slotData[i].values) {
                if (count == index) {
                    keys.push(l);
                    values.push(self.slotData[i].values[l]);
                    break;
                }

                count += 1;
            }
        }

        return { 'keys': keys, 'values': values };
    };

    /**
     *
     * Rolling slots
     *
     */

    this.setPosition = function (slot, pos) {
        self.slotEl[slot].slotYPosition = pos;
        self.slotEl[slot].style.webkitTransform = 'translate3d(0, ' + pos + 'px, 0)';
    };

    this.scrollStart = function (e) {
        // Find the clicked slot
        var xPos = e.targetTouches[0].clientX //- self.swSlots.offsetLeft;    // Clicked position minus left offset (should be 11px)

        // Find tapped slot
        var slot = 0;
        var el = self.swSlots;
        while (el) {
            slot += el.offsetLeft;
            el = el.offsetParent;
        }
        for (var i = 0; i < self.slotEl.length; i += 1) {
            slot += self.slotEl[i].slotWidth;

            if (xPos < slot) {
                self.activeSlot = i;
                break;
            }
        }

        // If slot is readonly do nothing
        if (self.slotData[self.activeSlot].style.match('readonly')) {
            self.swFrame.removeEventListener('touchmove', self, false);
            self.swFrame.removeEventListener('touchend', self, false);
            return false;
        }

        self.slotEl[self.activeSlot].removeEventListener('webkitTransitionEnd', self, false);   // Remove transition event (if any)
        self.slotEl[self.activeSlot].style.webkitTransitionDuration = '0';      // Remove any residual transition

        // Stop and hold slot position
        var theTransform = window.getComputedStyle(self.slotEl[self.activeSlot]).webkitTransform;
        theTransform = new WebKitCSSMatrix(theTransform).m42;
        if (theTransform != self.slotEl[self.activeSlot].slotYPosition) {
            self.setPosition(self.activeSlot, theTransform);
        }

        self.startY = e.targetTouches[0].clientY;
        self.scrollStartY = self.slotEl[self.activeSlot].slotYPosition;
        self.scrollStartTime = e.timeStamp;

        self.swFrame.addEventListener('touchmove', self, false);
        self.swFrame.addEventListener('touchend', self, false);

        return true;
    };

    this.scrollMove = function (e) {
        var topDelta = e.targetTouches[0].clientY - self.startY;

        if (self.slotEl[self.activeSlot].slotYPosition > 0 || self.slotEl[self.activeSlot].slotYPosition < self.slotEl[self.activeSlot].slotMaxScroll) {
            topDelta /= 2;
        }

        self.setPosition(self.activeSlot, self.slotEl[self.activeSlot].slotYPosition + topDelta);
        self.startY = e.targetTouches[0].clientY;

        // Prevent slingshot effect
        if (e.timeStamp - self.scrollStartTime > 80) {
            self.scrollStartY = self.slotEl[self.activeSlot].slotYPosition;
            self.scrollStartTime = e.timeStamp;
        }
        //self.getElementById('acslot').innerHTML = 'moving';
        //self.getElementById('newp').innerHTML = 'moving';
    };

    this.scrollEnd = function (e) {
        self.swFrame.removeEventListener('touchmove', self, false);
        self.swFrame.removeEventListener('touchend', self, false);

        // If we are outside of the boundaries, let's go back to the sheepfold
        if (self.slotEl[self.activeSlot].slotYPosition > 0 || self.slotEl[self.activeSlot].slotYPosition < self.slotEl[self.activeSlot].slotMaxScroll) {
            self.scrollTo(self.activeSlot, self.slotEl[self.activeSlot].slotYPosition > 0 ? 0 : self.slotEl[self.activeSlot].slotMaxScroll);
            return false;
        }

        // Lame formula to calculate a fake deceleration
        var scrollDistance = self.slotEl[self.activeSlot].slotYPosition - self.scrollStartY;

        // The drag session was too short
        if (scrollDistance < self.options.cellHeight / 1.5 && scrollDistance > -self.options.cellHeight / 1.5) {
            if (self.slotEl[self.activeSlot].slotYPosition % self.options.cellHeight) {
                self.scrollTo(self.activeSlot, Math.round(self.slotEl[self.activeSlot].slotYPosition / self.options.cellHeight) * self.options.cellHeight, '100ms');
            }

            return false;
        }

        var scrollDuration = e.timeStamp - self.scrollStartTime;

        var newDuration = (2 * scrollDistance / scrollDuration) / self.options.friction;
        var newScrollDistance = (self.options.friction / 2) * (newDuration * newDuration);

        if (newDuration < 0) {
            newDuration = -newDuration;
            newScrollDistance = -newScrollDistance;
        }

        var newPosition = self.slotEl[self.activeSlot].slotYPosition + newScrollDistance;

        if (newPosition > 0) {
            // Prevent the slot to be dragged outside the visible area (top margin)
            newPosition /= 2;
            newDuration /= 3;

            if (newPosition > self.swSlotWrapper.clientHeight / 4) {
                newPosition = self.swSlotWrapper.clientHeight / 4;
            }
        } else if (newPosition < self.slotEl[self.activeSlot].slotMaxScroll) {
            // Prevent the slot to be dragged outside the visible area (bottom margin)
            newPosition = (newPosition - self.slotEl[self.activeSlot].slotMaxScroll) / 2 + self.slotEl[self.activeSlot].slotMaxScroll;
            newDuration /= 3;

            if (newPosition < self.slotEl[self.activeSlot].slotMaxScroll - self.swSlotWrapper.clientHeight / 4) {
                newPosition = self.slotEl[self.activeSlot].slotMaxScroll - self.swSlotWrapper.clientHeight / 4;
            }
        } else {
            newPosition = Math.round(newPosition / self.options.cellHeight) * self.options.cellHeight;
        }

        self.scrollTo(self.activeSlot, Math.round(newPosition), Math.round(newDuration) + 'ms');
        return true;
    };

    this.scrollTo = function (slotNum, dest, runtime) {
        self.slotEl[slotNum].style.webkitTransitionDuration = runtime ? runtime : '100ms';
        self.setPosition(slotNum, dest ? dest : 0);

        // If we are outside of the boundaries go back to the sheepfold
        if (self.slotEl[slotNum].slotYPosition > 0 || self.slotEl[slotNum].slotYPosition < self.slotEl[slotNum].slotMaxScroll) {
            self.slotEl[slotNum].addEventListener('webkitTransitionEnd', self, false);
        }
    };

    this.scrollToValue = function (slot, value) {
        var yPos, count, i;

        self.slotEl[slot].removeEventListener('webkitTransitionEnd', self, false);
        self.slotEl[slot].style.webkitTransitionDuration = '0';

        count = 0;
        for (i in self.slotData[slot].values) {
            if (i == value) {
                yPos = count * self.options.cellHeight;
                self.setPosition(slot, yPos);
                break;
            }

            count -= 1;
        }
    };

    this.backWithinBoundaries = function (e) {
        e.target.removeEventListener('webkitTransitionEnd', self, false);

        self.scrollTo(e.target.slotPosition, e.target.slotYPosition > 0 ? 0 : e.target.slotMaxScroll, '150ms');
        return false;
    }

    this.init();

}<|endoftext|>