<commit_before>var React = require('react');
var classnames = require('classnames');

var BooleanColumn = React.createClass({
	render: function() {
		var value = this.props.data.fields[this.props.col.path];
		var iconClassName = classnames('ItemList__boolean-check octicon', {
			'is-checked octicon-check': value,
			'octicon-x': !value
		});
		return (

		);
	}
});

module.exports = BooleanColumn;
<commit_msg>cleanup boolean column<commit_after>import React from 'react';
import classnames from 'classnames';
import ItemsTableCell from '../../../admin/src/components/ItemsTableCell';
import ItemsTableValue from '../../../admin/src/components/ItemsTableValue';

var BooleanColumn = React.createClass({
	displayName: 'BooleanColumn',
	propTypes: {
		col: React.PropTypes.object,
		data: React.PropTypes.object,
	},
	renderValue () {
		let value = this.props.data.fields[this.props.col.path];
		let iconClassName = classnames('ItemList__boolean-check octicon', {
			'is-checked octicon-check': value,
			'octicon-x': !value
		});
		return (
			<ItemsTableValue truncate={false} field={this.props.col.path}>

			</ItemsTableValue>
		);
	},
	render () {
		return (
			<ItemsTableCell>
				{this.renderValue()}
			</ItemsTableCell>
		);
	}
});

module.exports = BooleanColumn;
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { Text, View } from 'react-native'

export default class AppContainer extends Component {
	constructor(props) {
		super(props)
		this.state = {
			store: {},
			theme: null
		}
	}

	render() {
		return (
			<View>
				<Text>Hello World</Text>
			</View>
		)
	}
}<commit_msg>added Drawer to AppContainer<commit_after>import React, { Component } from 'react'
import { Drawer } from 'native-base'
import { View } from 'react-native'

export default class AppContainer extends Component {
	constructor(props) {
		super(props)
		this.state = {
			toggled: false,
			store: {},
			theme: null
		}
	}

	toggleDrawer() {
		this.state.toggled ? this._drawer.close() : this._drawer.open()
	}

	openDrawer() {
		this.setState({toggled: true})
	}

	closeDrawer() {
		this.setState({toggled: false})
	}

	render() {
		return (
			<Drawer
				ref = {(ref) => this._drawer = ref}
				type = 'displace'
				content = { <View style = {{ backgroundColor: '#000', heigth: 1000 }} /> }
				onClose = {this.closeDrawer.bind(this)}
				onOpen = {this.openDrawer.bind(this)}
				openDrawerOffset = {0.2}
			</Drawer>
		)
	}
}<|endoftext|>


<commit_before>/*
 * Inspired from http://github.com/jeresig/sizzle/commit/7631f9c3f85e5fa72ac51532399cb593c2cdc71f
 * and this http://github.com/jeresig/sizzle/commit/5716360040a440041da19823964f96d025ca734b
 * and then http://dev.jquery.com/ticket/4512
 */

Element.implement({

	isHidden: function(){
		var w = this.offsetWidth, h = this.offsetHeight,
		force = (this.tagName.toLowerCase() === 'tr');
		return (w===0 && h===0 && !force) ? true : (w!==0 && h!==0 && !force) ? false : this.getStyle('display') === 'none';
	},

	isVisible: function(){
		return !this.isHidden();
	}

});

/*
 * Bespin wrapper
 */

Class.refactor(Editor, {
	options: {
		version: 0.6
	},
	initialize: function(options) {
		this.previous(options);
	},
	initEditor: function() {
		console.log('FD: instantiating ',this.options.element)
		this.textarea = $(this.options.element);
		var editor_id = this.textarea.get('id');
		console.log('FD: changing ', this.textarea);
		this.textarea.set('id',editor_id+'_textarea')
		console.log('FD: textarea id changed ', this.textarea.get('id'));
		this.element = new Element('div',{
			'text': this.textarea.get('text'),
			'id': editor_id,
		}).inject(this.textarea, 'before');
		console.log('FD: div element created ', this.element, 'with content ', this.element.get('text'));

		if (this.textarea.isHidden()) {
			this.element.hide();
			console.log('div hidden');
		}
		this.textarea.hide();
		console.log('FD: textarea hidden');
		this.bespin = tiki
			.require("Embedded")
			.useBespin(this.element,{syntax: "js"});
		console.log('FD: bespin instantiated');

		var boundOnBespinChange = this.onBespinChange.bind(this);
		this.bespin._editorView.getPath('layoutManager.textStorage')
			.addDelegate(SC.Object.create({
				textStorageEdited: boundOnBespinChange
			}));
		console.log('FD: bespin onChange hooked');
	},
	onBespinChange: function() {
		this.fireEvent('change');
		this.changed = true;
	},
	getContent: function() {
		this.textarea.set('text', this.bespin.value);
		return this.bespin.value;
	}
});
<commit_msg>bespin integrated<commit_after>/*
 * Inspired from http://github.com/jeresig/sizzle/commit/7631f9c3f85e5fa72ac51532399cb593c2cdc71f
 * and this http://github.com/jeresig/sizzle/commit/5716360040a440041da19823964f96d025ca734b
 * and then http://dev.jquery.com/ticket/4512
 */

Element.implement({

	isHidden: function(){
		var w = this.offsetWidth, h = this.offsetHeight,
		force = (this.tagName.toLowerCase() === 'tr');
		return (w===0 && h===0 && !force) ? true : (w!==0 && h!==0 && !force) ? false : this.getStyle('display') === 'none';
	},

	isVisible: function(){
		return !this.isHidden();
	}

});

/*
 * Bespin wrapper
 */

Class.refactor(Editor, {
	options: {
		version: 0.6
	},
	initialize: function(options) {
		this.previous(options);
	},
	initEditor: function() {
		console.log('FD: instantiating ',this.options.element)
		this.textarea = $(this.options.element);
		var editor_id = this.textarea.get('id');
		console.log('FD: changing ', this.textarea);
		this.textarea.set('id',editor_id+'_textarea')
		console.log('FD: textarea id changed ', this.textarea.get('id'));
		this.element = new Element('div',{
			'text': this.textarea.get('text'),
			'id': editor_id,
			'class': 'UI_Editor_Area'
		}).inject(this.textarea, 'before');
		console.log('FD: div element created ', this.element, 'with content ', this.element.get('text'));

		if (this.textarea.isHidden()) {
			this.element.hide();
			this.hidden = true;
			console.log('div hidden');
		}
		this.textarea.hide();
		console.log('FD: textarea hidden');
		(function() {
			this.bespin = tiki
				.require("Embedded")
				.useBespin(this.element, {syntax: "js"});
			console.log('FD: bespin instantiated');

			var boundOnBespinChange = this.onBespinChange.bind(this);
			this.bespin._editorView.getPath('layoutManager.textStorage')
				.addDelegate(SC.Object.create({
					textStorageEdited: boundOnBespinChange
				}));
			console.log('FD: bespin onChange hooked');
		}.bind(this)).delay(10);
	},
	onBespinChange: function() {
		this.fireEvent('change');
		this.changed = true;
	},
	getContent: function() {
		this.textarea.set('text', this.bespin.value);
		return this.bespin.value;
	}
});
<|endoftext|>


<commit_before>const { KnexTimeoutError } = require('../util/timeout');
const { timeout } = require('../util/timeout');
const {
  ensureConnectionCallback,
  ensureConnectionStreamCallback,
} = require('./internal/ensure-connection-callback');

let Transform;

// The "Runner" constructor takes a "builder" (query, schema, or raw)
// and runs through each of the query statements, calling any additional
// "output" method provided alongside the query and bindings.
class Runner {
  constructor(client, builder) {
    this.client = client;
    this.builder = builder;
    this.queries = [];

    // The "connection" object is set on the runner when
    // "run" is called.
    this.connection = undefined;
  }

  // "Run" the target, calling "toSQL" on the builder, returning
  // an object or array of queries to run, each of which are run on
  // a single connection.
  async run() {
    const runner = this;
    try {
      const res = await this.ensureConnection(ensureConnectionCallback);

      // Fire a single "end" event on the builder when
      // all queries have successfully completed.
      runner.builder.emit('end');
      return res;

      // If there are any "error" listeners, we fire an error event
      // and then re-throw the error to be eventually handled by
      // the promise chain. Useful if you're wrapping in a custom `Promise`.
    } catch (err) {
      if (runner.builder._events && runner.builder._events.error) {
        runner.builder.emit('error', err);
      }
      throw err;
    }
  }

  // Stream the result set, by passing through to the dialect's streaming
  // capabilities. If the options are
  stream(optionsOrHandler, handlerOrNil) {
    const firstOptionIsHandler =
      typeof optionsOrHandler === 'function' && arguments.length === 1;

    const options = firstOptionIsHandler ? {} : optionsOrHandler;
    const handler = firstOptionIsHandler ? optionsOrHandler : handlerOrNil;

    // Determines whether we emit an error or throw here.
    const hasHandler = typeof handler === 'function';

    // Lazy-load the "Transform" dependency.
    Transform = Transform || require('stream').Transform;

    const queryContext = this.builder.queryContext();
    let queryStream;

    const stream = new Transform({
      objectMode: true,
      transform: (chunk, _, callback) => {
        callback(null, this.client.postProcessResponse(chunk, queryContext));
      },
      destroy() {
        // For some reason destroy is not available for mssql on Node 14. Might be a problem with tedious: https://github.com/tediousjs/tedious/issues/1139
        if (queryStream && queryStream.destroy) {
          queryStream.destroy(new Error('stream destroyed'));
        }
      },
    });
    stream.on('pipe', (qs) => {
      queryStream = qs;
    });

    const connectionAcquirePromise = this.ensureConnection(
      ensureConnectionStreamCallback,
      {
        options,
        hasHandler,
        stream,
      }
    )
      // Emit errors on the stream if the error occurred before a connection
      // could be acquired.
      // If the connection was acquired, assume the error occurred in the client
      // code and has already been emitted on the stream. Don't emit it twice.
      .catch((err) => {
        if (!this.connection) {
          stream.emit('error', err);
        }
      });

    // If a function is passed to handle the stream, send the stream
    // there and return the promise, otherwise just return the stream
    // and the promise will take care of itself.
    if (hasHandler) {
      handler(stream);
      return connectionAcquirePromise;
    }
    return stream;
  }

  // Allow you to pipe the stream to a writable stream.
  pipe(writable, options) {
    return this.stream(options).pipe(writable);
  }

  // "Runs" a query, returning a promise. All queries specified by the builder are guaranteed
  // to run in sequence, and on the same connection, especially helpful when schema building
  // and dealing with foreign key constraints, etc.
  async query(obj) {
    const { __knexUid, __knexTxId } = this.connection;

    this.builder.emit('query', Object.assign({ __knexUid, __knexTxId }, obj));

    const runner = this;
    const queryContext = this.builder.queryContext();
    // query-error events are emitted before the queryPromise continuations.
    // pass queryContext into client.query so it can be raised properly.
    if (obj !== null && typeof obj === 'object') {
      obj.queryContext = queryContext;
    }
    let queryPromise = this.client.query(this.connection, obj);

    if (obj.timeout) {
      queryPromise = timeout(queryPromise, obj.timeout);
    }

    // Await the return value of client.processResponse; in the case of sqlite3's
    // dropColumn()/renameColumn(), it will be a Promise for the transaction
    // containing the complete rename procedure.
    return queryPromise
      .then((resp) => this.client.processResponse(resp, runner))
      .then((processedResponse) => {
        const postProcessedResponse = this.client.postProcessResponse(
          processedResponse,
          queryContext
        );

        this.builder.emit(
          'query-response',
          postProcessedResponse,
          Object.assign({ __knexUid, __knexTxId }, obj),
          this.builder
        );

        this.client.emit(
          'query-response',
          postProcessedResponse,
          Object.assign({ __knexUid, __knexTxId }, obj),
          this.builder
        );

        return postProcessedResponse;
      })
      .catch((error) => {
        if (!(error instanceof KnexTimeoutError)) {
          return Promise.reject(error);
        }
        const { timeout, sql, bindings } = obj;

        let cancelQuery;
        if (obj.cancelOnTimeout) {
          cancelQuery = this.client.cancelQuery(this.connection);
        } else {
          // If we don't cancel the query, we need to mark the connection as disposed so that
          // it gets destroyed by the pool and is never used again. If we don't do this and
          // return the connection to the pool, it will be useless until the current operation
          // that timed out, finally finishes.
          this.connection.__knex__disposed = error;
          cancelQuery = Promise.resolve();
        }

        return cancelQuery
          .catch((cancelError) => {
            // If the cancellation failed, we need to mark the connection as disposed so that
            // it gets destroyed by the pool and is never used again. If we don't do this and
            // return the connection to the pool, it will be useless until the current operation
            // that timed out, finally finishes.
            this.connection.__knex__disposed = error;

            // cancellation failed
            throw Object.assign(cancelError, {
              message: `After query timeout of ${timeout}ms exceeded, cancelling of query failed.`,
              sql,
              bindings,
              timeout,
            });
          })
          .then(() => {
            // cancellation succeeded, rethrow timeout error
            throw Object.assign(error, {
              message: `Defined query timeout of ${timeout}ms exceeded when running query.`,
              sql,
              bindings,
              timeout,
            });
          });
      })
      .catch((error) => {
        this.builder.emit(
          'query-error',
          error,
          Object.assign({ __knexUid, __knexTxId, queryContext }, obj)
        );
        throw error;
      });
  }

  // In the case of the "schema builder" we call `queryArray`, which runs each
  // of the queries in sequence.
  async queryArray(queries) {
    if (queries.length === 1) {
      const query = queries[0];

      if (!query.statementsProducer) {
        return this.query(query);
      }
      const statements = await query.statementsProducer(
        undefined,
        this.connection
      );
      return this.queryArray(statements);
    }

    const results = [];
    for (const query of queries) {
      results.push(await this.query(query));
    }
    return results;
  }

  // Check whether there's a transaction flag, and that it has a connection.
  async ensureConnection(cb, cbParams) {
    // Use override from a builder if passed
    if (this.builder._connection) {
      this.connection = this.builder._connection;
    }

    if (this.connection) {
      return cb(this, cbParams);
    }

    let acquiredConnection;
    try {
      acquiredConnection = await this.client.acquireConnection();
    } catch (error) {
      if (!(error instanceof KnexTimeoutError)) {
        return Promise.reject(error);
      }
      if (this.builder) {
        error.sql = this.builder.sql;
        error.bindings = this.builder.bindings;
      }
      throw error;
    }
    try {
      this.connection = acquiredConnection;
      return await cb(this, cbParams);
    } finally {
      await this.client.releaseConnection(acquiredConnection);
    }
  }
}

module.exports = Runner;
<commit_msg>Convert produced statements to objects before querying (#4323)<commit_after>const { KnexTimeoutError } = require('../util/timeout');
const { timeout } = require('../util/timeout');
const {
  ensureConnectionCallback,
  ensureConnectionStreamCallback,
} = require('./internal/ensure-connection-callback');

let Transform;

// The "Runner" constructor takes a "builder" (query, schema, or raw)
// and runs through each of the query statements, calling any additional
// "output" method provided alongside the query and bindings.
class Runner {
  constructor(client, builder) {
    this.client = client;
    this.builder = builder;
    this.queries = [];

    // The "connection" object is set on the runner when
    // "run" is called.
    this.connection = undefined;
  }

  // "Run" the target, calling "toSQL" on the builder, returning
  // an object or array of queries to run, each of which are run on
  // a single connection.
  async run() {
    const runner = this;
    try {
      const res = await this.ensureConnection(ensureConnectionCallback);

      // Fire a single "end" event on the builder when
      // all queries have successfully completed.
      runner.builder.emit('end');
      return res;

      // If there are any "error" listeners, we fire an error event
      // and then re-throw the error to be eventually handled by
      // the promise chain. Useful if you're wrapping in a custom `Promise`.
    } catch (err) {
      if (runner.builder._events && runner.builder._events.error) {
        runner.builder.emit('error', err);
      }
      throw err;
    }
  }

  // Stream the result set, by passing through to the dialect's streaming
  // capabilities. If the options are
  stream(optionsOrHandler, handlerOrNil) {
    const firstOptionIsHandler =
      typeof optionsOrHandler === 'function' && arguments.length === 1;

    const options = firstOptionIsHandler ? {} : optionsOrHandler;
    const handler = firstOptionIsHandler ? optionsOrHandler : handlerOrNil;

    // Determines whether we emit an error or throw here.
    const hasHandler = typeof handler === 'function';

    // Lazy-load the "Transform" dependency.
    Transform = Transform || require('stream').Transform;

    const queryContext = this.builder.queryContext();
    let queryStream;

    const stream = new Transform({
      objectMode: true,
      transform: (chunk, _, callback) => {
        callback(null, this.client.postProcessResponse(chunk, queryContext));
      },
      destroy() {
        // For some reason destroy is not available for mssql on Node 14. Might be a problem with tedious: https://github.com/tediousjs/tedious/issues/1139
        if (queryStream && queryStream.destroy) {
          queryStream.destroy(new Error('stream destroyed'));
        }
      },
    });
    stream.on('pipe', (qs) => {
      queryStream = qs;
    });

    const connectionAcquirePromise = this.ensureConnection(
      ensureConnectionStreamCallback,
      {
        options,
        hasHandler,
        stream,
      }
    )
      // Emit errors on the stream if the error occurred before a connection
      // could be acquired.
      // If the connection was acquired, assume the error occurred in the client
      // code and has already been emitted on the stream. Don't emit it twice.
      .catch((err) => {
        if (!this.connection) {
          stream.emit('error', err);
        }
      });

    // If a function is passed to handle the stream, send the stream
    // there and return the promise, otherwise just return the stream
    // and the promise will take care of itself.
    if (hasHandler) {
      handler(stream);
      return connectionAcquirePromise;
    }
    return stream;
  }

  // Allow you to pipe the stream to a writable stream.
  pipe(writable, options) {
    return this.stream(options).pipe(writable);
  }

  // "Runs" a query, returning a promise. All queries specified by the builder are guaranteed
  // to run in sequence, and on the same connection, especially helpful when schema building
  // and dealing with foreign key constraints, etc.
  async query(obj) {
    const { __knexUid, __knexTxId } = this.connection;

    this.builder.emit('query', Object.assign({ __knexUid, __knexTxId }, obj));

    const runner = this;
    const queryContext = this.builder.queryContext();
    // query-error events are emitted before the queryPromise continuations.
    // pass queryContext into client.query so it can be raised properly.
    if (obj !== null && typeof obj === 'object') {
      obj.queryContext = queryContext;
    }
    let queryPromise = this.client.query(this.connection, obj);

    if (obj.timeout) {
      queryPromise = timeout(queryPromise, obj.timeout);
    }

    // Await the return value of client.processResponse; in the case of sqlite3's
    // dropColumn()/renameColumn(), it will be a Promise for the transaction
    // containing the complete rename procedure.
    return queryPromise
      .then((resp) => this.client.processResponse(resp, runner))
      .then((processedResponse) => {
        const postProcessedResponse = this.client.postProcessResponse(
          processedResponse,
          queryContext
        );

        this.builder.emit(
          'query-response',
          postProcessedResponse,
          Object.assign({ __knexUid, __knexTxId }, obj),
          this.builder
        );

        this.client.emit(
          'query-response',
          postProcessedResponse,
          Object.assign({ __knexUid, __knexTxId }, obj),
          this.builder
        );

        return postProcessedResponse;
      })
      .catch((error) => {
        if (!(error instanceof KnexTimeoutError)) {
          return Promise.reject(error);
        }
        const { timeout, sql, bindings } = obj;

        let cancelQuery;
        if (obj.cancelOnTimeout) {
          cancelQuery = this.client.cancelQuery(this.connection);
        } else {
          // If we don't cancel the query, we need to mark the connection as disposed so that
          // it gets destroyed by the pool and is never used again. If we don't do this and
          // return the connection to the pool, it will be useless until the current operation
          // that timed out, finally finishes.
          this.connection.__knex__disposed = error;
          cancelQuery = Promise.resolve();
        }

        return cancelQuery
          .catch((cancelError) => {
            // If the cancellation failed, we need to mark the connection as disposed so that
            // it gets destroyed by the pool and is never used again. If we don't do this and
            // return the connection to the pool, it will be useless until the current operation
            // that timed out, finally finishes.
            this.connection.__knex__disposed = error;

            // cancellation failed
            throw Object.assign(cancelError, {
              message: `After query timeout of ${timeout}ms exceeded, cancelling of query failed.`,
              sql,
              bindings,
              timeout,
            });
          })
          .then(() => {
            // cancellation succeeded, rethrow timeout error
            throw Object.assign(error, {
              message: `Defined query timeout of ${timeout}ms exceeded when running query.`,
              sql,
              bindings,
              timeout,
            });
          });
      })
      .catch((error) => {
        this.builder.emit(
          'query-error',
          error,
          Object.assign({ __knexUid, __knexTxId, queryContext }, obj)
        );
        throw error;
      });
  }

  // In the case of the "schema builder" we call `queryArray`, which runs each
  // of the queries in sequence.
  async queryArray(queries) {
    if (queries.length === 1) {
      const query = queries[0];

      if (!query.statementsProducer) {
        return this.query(query);
      }

      const statements = await query.statementsProducer(
        undefined,
        this.connection
      );
      const queryObjects = statements.map((statement) => ({
        sql: statement,
        bindings: query.bindings,
      }));

      return this.queryArray(queryObjects);
    }

    const results = [];
    for (const query of queries) {
      results.push(await this.query(query));
    }
    return results;
  }

  // Check whether there's a transaction flag, and that it has a connection.
  async ensureConnection(cb, cbParams) {
    // Use override from a builder if passed
    if (this.builder._connection) {
      this.connection = this.builder._connection;
    }

    if (this.connection) {
      return cb(this, cbParams);
    }

    let acquiredConnection;
    try {
      acquiredConnection = await this.client.acquireConnection();
    } catch (error) {
      if (!(error instanceof KnexTimeoutError)) {
        return Promise.reject(error);
      }
      if (this.builder) {
        error.sql = this.builder.sql;
        error.bindings = this.builder.bindings;
      }
      throw error;
    }
    try {
      this.connection = acquiredConnection;
      return await cb(this, cbParams);
    } finally {
      await this.client.releaseConnection(acquiredConnection);
    }
  }
}

module.exports = Runner;
<|endoftext|>


<commit_before>'use strict';

/**
 * The floppy drive controller interface. Expects to communicate with
 * a device according to the specification outlined in the project
 * wiki.
 */

var EventEmitter = module.require('events').EventEmitter,
    SerialPort = module.require('serialport').SerialPort;

/**
 * Initializes the controller.
 *
 * @constructor FloppyController
 * @param {string} port
 *   The serial interface (e.g. '/dev/tty0').
 */
var FloppyController = module.exports = function(port) {
  var self = this;

  // Set up an internal event system to respond asynchronously to
  // serial information:
  this.events = new EventEmitter();

  // Cache the ready state of the device:
  this.ready = false;

  this.events.on('ready', function() {
    self.ready = true;
  });

  // Connect to the device and attempt to open the connection
  // immediately:
  this.serial = new SerialPort(port, {
    baudrate: 9600,
    bufferSize: 2
  }, true)
  // @todo deal with errors for all of these
  .on('open', function(error) {
    self.events.emit('open');

    self.setFrequency(0, 220);
  })
  .on('data', function(data) {
    self.processData(data, self.events);
  })
  .on('close', function(error) {
    self.events.emit('close');
  })
  .on('error', function(error) {
    // fail spectacularly:
    console.log(error);
  });
};

/**
 * Emits events as serial data is received.
 *
 * @param {Buffer} data
 *   The received bytes.
 * @param {EventEmitter} emitter
 *   The event recipient.
 */
FloppyController.prototype.processData = function(data, emitter) {
  switch (data[0]) {
    // Ready:
    case 0:
      emitter.emit('ready');
      break;

    // Number of active drives:
    case 8:
      emitter.emit('count', data[1]);
      break;
  }
};

/**
 * Requests a ready confirmation.
 *
 * @returns {Promise}
 *   A promise that resolves when the ready state of the drive is
 *   known.
 */
FloppyController.prototype.getReadyState = function() {
  var self = this;

  return new Promise(function(fulfill, reject) {
    if (self.ready) {
      fulfill(true);
    }
    else {
      self.serial.write([0, 0, 0, 0]);

      self.events.on('ready', function() {
        fulfill(true);
      });
    }
  });
};

/**
 * Requests the number of active drives. (This is hardcoded into the
 * Arduino control code.)
 *
 * @returns {Promise}
 *   A promise that resolves when the number of active drives is
 *   known.
 */
FloppyController.prototype.getDriveCount = function() {
  var self = this;

  return new Promise(function(fulfill, reject) {
    self.getReadyState().then(function() {
      self.serial.write([8, 0, 0, 0]);

      self.events.on('count', function(count) {
        fulfill(count);
      });
    });
  });
};

/**
 * Sets the frequency of a drive.
 *
 * @param {number} drive
 *   The number of the target drive.
 * @param {number} frequency
 *   The target frequency. 0 will silence the drive.
 */
FloppyController.prototype.setFrequency = function(drive, frequency) {
  var self = this;

  // @todo check ranges
  var f1 = frequency % 255,
      f2 = frequency > 255 ? (frequency - 255) % 255 : 0;

  this.getReadyState().then(function() {
    self.serial.write([16, drive, f1, f2]);
  });
};
<commit_msg>Cleaned up JSDoc for FloppyController<commit_after>'use strict';

var EventEmitter = module.require('events').EventEmitter,
    SerialPort = module.require('serialport').SerialPort;

/**
 * Initializes the controller.
 *
 * @class FloppyController
 * @classdesc
 *   The floppy drive controller interface. Expects to communicate with a device
 *   {@link https://github.com/dstelljes/umm-floppy-choir/wiki/Serial-interface
 *   according to the specification in the project wiki.}
 *
 * @param {string} port
 *   The serial interface (e.g. '/dev/tty0').
 */
var FloppyController = module.exports = function(port) {
  var self = this;

  // Set up an internal event system to respond asynchronously to serial
  // information:
  this.events = new EventEmitter();

  // Cache the ready state of the device:
  this.ready = false;

  this.events.on('ready', function() {
    self.ready = true;
  });

  // Connect to the device and attempt to open the connection immediately:
  this.serial = new SerialPort(port, {
    baudrate: 9600,
    bufferSize: 2
  }, true)
  // @todo deal with errors for all of these
  .on('open', function(error) {
    self.events.emit('open');
  })
  .on('data', function(data) {
    self.processBuffer(data, self.events);
  })
  .on('close', function(error) {
    self.events.emit('close');
  })
  .on('error', function(error) {
    // fail spectacularly:
    console.log(error);
  });
};

/**
 * Converts a response sequence into an event on the given emitter.
 *
 * @param {Buffer} data
 *   The received bytes.
 * @param {EventEmitter} emitter
 *   The event recipient.
 */
FloppyController.processBuffer = function(data, emitter) {
  switch (data[0]) {
    // Ready:
    case 0:
      emitter.emit('ready');
      break;

    // Number of active drives:
    case 8:
      emitter.emit('count', data[1]);
      break;
  }
};

/**
 * Requests a ready confirmation.
 *
 * @example
 *   var fc = FloppyController('/dev/tty0');
 *   fc.getReadyState().then(function() {
 *     console.log('The drive is ready!');
 *   });
 *
 * @returns {Promise}
 *   A promise that resolves when the ready state of the drive is known.
 */
FloppyController.prototype.getReadyState = function() {
  var self = this;

  return new Promise(function(fulfill, reject) {
    if (self.ready) {
      fulfill(true);
    }
    else {
      self.serial.write([0, 0, 0, 0]);

      self.events.on('ready', function() {
        fulfill(true);
      });
    }
  });
};

/**
 * Requests the number of active drives. (This is hardcoded into the Arduino
 * control code.)
 *
 * @example
 *   var fc = FloppyController('/dev/tty0');
 *   fc.getDriveCount().then(function(count) {
 *     console.log('There are %d drives active.', count);
 *   });
 *
 * @returns {Promise}
 *   A promise that resolves when the number of active drives is
 *   known.
 */
FloppyController.prototype.getDriveCount = function() {
  var self = this;

  return new Promise(function(fulfill, reject) {
    self.getReadyState().then(function() {
      self.serial.write([8, 0, 0, 0]);

      self.events.on('count', function(count) {
        fulfill(count);
      });
    });
  });
};

/**
 * Sets the frequency of a drive.
 *
 * @example
 *   var fc = FloppyController('/dev/tty0');
 *   // plays a tuning A for two seconds
 *   fc.setFrequency(0, 440);
 *   setTimeout(function() {
 *     fc.setFrequency(0, 0);
 *   }, 2000);
 *
 * @param {number} drive
 *   The number of the target drive.
 * @param {number} frequency
 *   The target frequency (Hz). 0 will silence the drive.
 */
FloppyController.prototype.setFrequency = function(drive, frequency) {
  var self = this;

  // @todo check ranges
  var f1 = frequency % 255,
      f2 = frequency > 255 ? (frequency - 255) % 255 : 0;

  this.getReadyState().then(function() {
    self.serial.write([16, drive, f1, f2]);
  });
};
<|endoftext|>


<commit_before>import _        from "underscore";
import $        from "jquery";
import BaseView from "../BaseView";

export default class SwitchView extends BaseView {

	className() { return 'ui-switch'; }

	constructor(options) {
		super(options);

		this.options = _.defaults(this.options || {},  {
			field:    null, // Set field name if SwitchView is wrapped into a form
			fieldId:  null,
			disabled: false,
			invalid:  false,
			selected: null,
			buttons:  []
		});

		this.addEvents({
			'click button': 'onSwitch'
		});

		if (!options.buttons)
			throw new Error('Cannot initialize SwitchView without buttons');

		this.buttons = options.buttons;
		this.selectButton(options.selected || this.buttons[0].value);
	}

	onRender(rendered) {
		if ( rendered ) return this;

		this.undelegateEvents();
		this.$el.empty();
		_(this.buttons).each((aButton) => {
			this.addButton(aButton);
		});
		this.delegateEvents();

		// If SwitchView is wrappet into a form, then create input field
		if (this.options.field) {
			this.cache.$input = $('<input type="hidden" />').attr('data-field', this.options.field);
			this.cache.$input.val( this.selectedButton ? this.selectedButton.value : this.buttons[0].value);

			if (this.options.fieldId)
				this.cache.$input.attr('id', this.options.fieldId);

			this.$el.append( this.cache.$input );

			if (this.options.invalid)
				this.$el.append($('<div>').addClass('ui-input-invalid').attr('data-invalid-field', this.options.field));
		}

		if (this.options.disabled)
			this.setEnabled(false);

		return this;
	}

	addButton(button) {
		let $button = $('<button>').addClass('button').attr({ 'data-value': button.value });

		if (button.label instanceof BaseView) {
			this.addSubView(`button-${button.value}-label`, button.label);
			$button.append(button.label.el);
			button.label.render();
		} else if (button.label) {
			$button.append(button.label);
		}

		if (button.icon)
			$button.append( $('<i>').addClass(button.icon) );

		if (this.selectedButton.value == button.value)
			$button.addClass('active');

		this.cache[button.value] = $button;

		this.$el.append($button);

		return this;
	}

	// changeButton(value, buttonChanges) {
	// 	const $button = this.cache[button.value];
	//
	// 	if (!$button)
	// 		return this;
	//
	// 	if (!buttonChanges)
	// 		return this;
	//
	// 	if (buttonChanges.label instanceof BaseView) {
	// 		this.removeSubView(`button-${button.value}-label`);
	// 		$button.append(buttonChanges.label.el);
	// 		buttonChanges.label.render();
	// 	}
	// 	else if (buttonChanges.label) {
	// 		$button
	// 			.empty()
	// 			.append(buttonChanges.label);
	// 	}
	//
	// 	if (buttonChanges.icon)
	// 		$button.append($('<i>').addClass(buttonChanges.icon));
	// }

	selectButton(value, options) {
		options = _.defaults(options || {}, {
			silent: false
		});

		if (this.selectedButton && this.cache[this.selectedButton.value])
			this.cache[this.selectedButton.value].removeClass('active');

		this.selectedButton = _.find(this.buttons, function (aButton) {
			return aButton.value == value;
		}, this);

		if (this.cache[value])
			this.cache[value].addClass('active');

		if (this.options.field && this.cache.$input) {
			this.cache.$input.val(value);

			if (!options.silent)
				this.cache.$input.trigger('input').trigger('change');
		}

		if (!options.silent)
			this.trigger('select', value, this.selectedButton);
	}

	onSwitch(e) {
		e.preventDefault();

		if (this.disabled) return;

		let $target = $(e.target);
		let value = $target.attr('data-value');
		if (!value)
			value = $target.parent().attr('data-value');
		this.selectButton(value);
	}

	getValue() {
		return this.selectedButton.value;
	}

	setEnabled(enabled) {
		// Enable
		if (enabled && this.disabled) {
			this.$el.removeClass("disabled");
			this.disabled = false;
			if (this.cache.$input)
				this.cache.$input.removeAttr("disabled");
		}
		// Disable
		if (!enabled && !this.disabled) {
			this.$el.addClass("disabled");
			this.disabled = true;
			if (this.cache.$input)
				this.cache.$input.attr("disabled", "disabled");
		}
	}

};
<commit_msg>Added SwitchView input change support<commit_after>import _        from "underscore";
import $        from "jquery";
import BaseView from "../BaseView";

export default class SwitchView extends BaseView {

	className() { return 'ui-switch'; }

	constructor(options) {
		super(options);

		this.options = _.defaults(this.options || {},  {
			field:    null, // Set field name if SwitchView is wrapped into a form
			fieldId:  null,
			disabled: false,
			invalid:  false,
			selected: null,
			buttons:  []
		});

		this.addEvents({
			'click button': 'onSwitch',
			'input input': 'onInputChange'
		});

		if (!options.buttons)
			throw new Error('Cannot initialize SwitchView without buttons');

		this.buttons = options.buttons;
		this.selectButton(options.selected || this.buttons[0].value);
	}

	onRender(rendered) {
		if ( rendered ) return this;

		this.undelegateEvents();
		this.$el.empty();
		_(this.buttons).each((aButton) => {
			this.addButton(aButton);
		});
		this.delegateEvents();

		// If SwitchView is wrappet into a form, then create input field
		if (this.options.field) {
			this.cache.$input = $('<input type="hidden" />').attr('data-field', this.options.field);
			this.cache.$input.val( this.selectedButton ? this.selectedButton.value : this.buttons[0].value);

			if (this.options.fieldId)
				this.cache.$input.attr('id', this.options.fieldId);

			this.$el.append( this.cache.$input );

			if (this.options.invalid)
				this.$el.append($('<div>').addClass('ui-input-invalid').attr('data-invalid-field', this.options.field));
		}

		if (this.options.disabled)
			this.setEnabled(false);

		return this;
	}

	addButton(button) {
		let $button = $('<button>').addClass('button').attr({ 'data-value': button.value });

		if (button.label instanceof BaseView) {
			this.addSubView(`button-${button.value}-label`, button.label);
			$button.append(button.label.el);
			button.label.render();
		} else if (button.label) {
			$button.append(button.label);
		}

		if (button.icon)
			$button.append( $('<i>').addClass(button.icon) );

		if (this.selectedButton.value == button.value)
			$button.addClass('active');

		this.cache[button.value] = $button;

		this.$el.append($button);

		return this;
	}

	// changeButton(value, buttonChanges) {
	// 	const $button = this.cache[button.value];
	//
	// 	if (!$button)
	// 		return this;
	//
	// 	if (!buttonChanges)
	// 		return this;
	//
	// 	if (buttonChanges.label instanceof BaseView) {
	// 		this.removeSubView(`button-${button.value}-label`);
	// 		$button.append(buttonChanges.label.el);
	// 		buttonChanges.label.render();
	// 	}
	// 	else if (buttonChanges.label) {
	// 		$button
	// 			.empty()
	// 			.append(buttonChanges.label);
	// 	}
	//
	// 	if (buttonChanges.icon)
	// 		$button.append($('<i>').addClass(buttonChanges.icon));
	// }

	selectButton(value, options) {
		options = _.defaults(options || {}, {
			silent: false
		});

		if (this.selectedButton && this.cache[this.selectedButton.value])
			this.cache[this.selectedButton.value].removeClass('active');

		this.selectedButton = _.find(this.buttons, function (aButton) {
			return aButton.value == value;
		}, this);

		if (this.cache[value])
			this.cache[value].addClass('active');

		if (this.options.field && this.cache.$input) {
			this.cache.$input.val(value);

			if (!options.silent)
				this.cache.$input.trigger('input').trigger('change');
		}

		if (!options.silent)
			this.trigger('select', value, this.selectedButton);
	}

	onSwitch(e) {
		e.preventDefault();

		if (this.disabled) return;

		let $target = $(e.target);
		let value = $target.attr('data-value');
		if (!value)
			value = $target.parent().attr('data-value');
		this.selectButton(value);
	}

	getValue() {
		return this.selectedButton.value;
	}

	setEnabled(enabled) {
		// Enable
		if (enabled && this.disabled) {
			this.$el.removeClass("disabled");
			this.disabled = false;
			if (this.cache.$input)
				this.cache.$input.removeAttr("disabled");
		}
		// Disable
		if (!enabled && !this.disabled) {
			this.$el.addClass("disabled");
			this.disabled = true;
			if (this.cache.$input)
				this.cache.$input.attr("disabled", "disabled");
		}
	}

	onInputChange() {
		if (this.cache.$input) {
			this.selectButton(this.cache.$input.val(), { silent: true });
		}
	}

};
<|endoftext|>


<commit_before>$(function() {
    // The link to colorScheme.js is hidden by default
    // to not confuse users who don't have JavaScript
    // enabled. Make it visible now
    $('li a[href="colorScheme.html"]').css("display", "inline");

    // UI elements in colorScheme.html
    // these vars will not select anything if we are in a
    // different page
    var headerSelect = $("#headerSelect");
    var titleSelect = $("#titleSelect");
    var mainBodySelect = $("#mainBodySelect");
    var navSelect = $("#navSelect");
    var backgroundSelect = $("#backgroundSelect");

    // read the options for the selects into an array to use for
    // the random color button
    var selectOptions = [];
    $('#headerSelect > option').each(function(i, selected) {
        selectOptions[i] = $(selected).val();
    });

    function ColorSet(headerHex, titleHex, mainHex, sideHex, bgHex) {
        // Constructor for an object that stores a set of the hexcodes
        // for a specified color scheme
        this.headerHex = headerHex;
        this.titleHex = titleHex;
        this.mainHex = mainHex;
        this.sideHex = sideHex;
        this.bgHex = bgHex;
    }

    function defaultColorSet() {
        return new ColorSet('#0099FF', '#FFCC00', '#FFFFFF',
                            '#808080', '#B0C4DE');
    }

    function changeColor(selector, color) {
        // changes the background-color of the elements covered
        // by selector to color
        $(selector).css("background-color", color);
    }

    function animateColor(selector, color) {
        // like changeColor, but does the change with an
        // animation, using jquery.colors.js
        // we only animate the initial change of color when the
        // user first picks the new color, all other changes of
        // the css value are done instantly with changeColor
        $(selector).animate({backgroundColor: color}, 'slow');
    }

    function save() {
        var newColors = new ColorSet(headerSelect.val(),
                                     titleSelect.val(),
                                     mainBodySelect.val(),
                                     navSelect.val(),
                                     backgroundSelect.val());
        $.cookie("color", JSON.stringify(newColors),{expires: 7});
    }

    function selectChangeEvent(selector, color)
    {
        // event that is called when one of the selects changes.
        // The elements specified by selector animate a color
        // change to color, and the cookie is updated
        // accordingly
        animateColor(selector, color);
        save();
    }

    headerSelect.change(function() {
        selectChangeEvent("header", headerSelect.val());
    });

    titleSelect.change(function() {
        selectChangeEvent("h1", titleSelect.val());
    });

    mainBodySelect.change(function() {
        selectChangeEvent("#mainText", mainBodySelect.val());
    });

    navSelect.change(function() {
        // In case the user highlighted nav li's via
        // highlightNav.js, change them too
        selectChangeEvent("nav, nav li", navSelect.val());
    });

    backgroundSelect.change(function() {
        selectChangeEvent("body", backgroundSelect.val());
    });

    function set(colSet, shouldAnimate) {
        // sets the selects' names and the background colors as
        // specified by colSet, animates if shouldAnimate is
        // true, else just changes the colors
        colSet = $.extend(defaultColorSet(), colSet);//remove null values just in case
        headerSelect.val(colSet.headerHex);
        titleSelect.val(colSet.titleHex);
        mainBodySelect.val(colSet.mainHex);
        navSelect.val(colSet.sideHex);
        backgroundSelect.val(colSet.bgHex);

        var colorFun = shouldAnimate ? animateColor : changeColor;

        colorFun("header", colSet.headerHex);
        colorFun("h1", colSet.titleHex);
        colorFun("#mainText", colSet.mainHex);
        colorFun("nav, nav li", colSet.sideHex);
        colorFun("body", colSet.bgHex);
        return true;
    }

    function resetCookie() {
        $.cookie("color", null);
    }

    $("#resetButton").click(function() {
        set(defaultColorSet(), true);
        resetCookie();
    });

    function randomNum(maxVal) {
        return Math.floor(Math.random() * maxVal);
    }

    $("#listRandom").click(function() {
        var randomColors = [];
        var numOptions = selectOptions.length;
        for (var i = 0; i < numOptions; i++) {
            randomColors[i] = selectOptions[randomNum(numOptions)];
        }
        var randCols = new ColorSet(randomColors[0], randomColors[1],
                                    randomColors[2], randomColors[3],
                                    randomColors[4]);
        set(randCols, true);
        save();
    });

    $("#expRandom").click(function() {

        function randRGBVal() {
            // FF in hex:
            var maxVal = 255;
            var colHex = randomNum(maxVal).toString(16);
            // pad it with leading 0's if needed
            while (colHex.length < 2) {
                colHex = "0" + colHex;
            }
            return colHex;
        }

        function getColorHex() {
            var red = randRGBVal();
            var green = randRGBVal();
            var blue = randRGBVal();
            return "#" + red + green + blue;
        }
        var newCols = [];
        $("select").each(function(index) {
            var col = getColorHex().toUpperCase();
            var self = $(this);
	    var newColOption = $("<option>", {
	                         value : col,
	                         html : col});
	    self.append(newColOption);
	    self.val(col);
	    newCols[index] = col;
	});
	set(new ColorSet(newCols[0], newCols[1],
			 newCols[2], newCols[3],
			 newCols[4]), true);
        save();
    });

    function setColorsFromCookie() {
        // reads color and sets the colors accordingly
        // does NOT animate the color change, as the page should
        // load with the changed colors, and the user should not
        // see the original ones (they have already seen an
        // animation)
        set(JSON.parse($.cookie("color")), false);
    }

    setColorsFromCookie();

});
<commit_msg>Updated indentation in colors.js<commit_after>$(function() {
  // The link to colorScheme.js is hidden by default
  // to not confuse users who don't have JavaScript
  // enabled. Make it visible now
  $('li a[href="colorScheme.html"]').css("display", "inline");

  // UI elements in colorScheme.html
  // these vars will not select anything if we are in a
  // different page
  var headerSelect = $("#headerSelect");
  var titleSelect = $("#titleSelect");
  var mainBodySelect = $("#mainBodySelect");
  var navSelect = $("#navSelect");
  var backgroundSelect = $("#backgroundSelect");

  // read the options for the selects into an array to use for
  // the random color button
  var selectOptions = [];
  $('#headerSelect > option').each(function(i, selected) {
    selectOptions[i] = $(selected).val();
  });

  function ColorSet(headerHex, titleHex, mainHex, sideHex, bgHex) {
    // Constructor for an object that stores a set of the hexcodes
    // for a specified color scheme
    this.headerHex = headerHex;
    this.titleHex = titleHex;
    this.mainHex = mainHex;
    this.sideHex = sideHex;
    this.bgHex = bgHex;
  }

  function defaultColorSet() {
    return new ColorSet('#0099FF', '#FFCC00', '#FFFFFF',
        '#808080', '#B0C4DE');
  }

  function changeColor(selector, color) {
    // changes the background-color of the elements covered
    // by selector to color
    $(selector).css("background-color", color);
  }

  function animateColor(selector, color) {
    // like changeColor, but does the change with an
    // animation, using jquery.colors.js
    // we only animate the initial change of color when the
    // user first picks the new color, all other changes of
    // the css value are done instantly with changeColor
    $(selector).animate({backgroundColor: color}, 'slow');
  }

  function save() {
    var newColors = new ColorSet(headerSelect.val(),
        titleSelect.val(),
        mainBodySelect.val(),
        navSelect.val(),
        backgroundSelect.val());
    $.cookie("color", JSON.stringify(newColors),{expires: 7});
  }

  function selectChangeEvent(selector, color)
  {
    // event that is called when one of the selects changes.
    // The elements specified by selector animate a color
    // change to color, and the cookie is updated
    // accordingly
    animateColor(selector, color);
    save();
  }

  headerSelect.change(function() {
    selectChangeEvent("header", headerSelect.val());
  });

  titleSelect.change(function() {
    selectChangeEvent("h1", titleSelect.val());
  });

  mainBodySelect.change(function() {
    selectChangeEvent("#mainText", mainBodySelect.val());
  });

  navSelect.change(function() {
    // In case the user highlighted nav li's via
    // highlightNav.js, change them too
    selectChangeEvent("nav, nav li", navSelect.val());
  });

  backgroundSelect.change(function() {
    selectChangeEvent("body", backgroundSelect.val());
  });

  function set(colSet, shouldAnimate) {
    // sets the selects' names and the background colors as
    // specified by colSet, animates if shouldAnimate is
    // true, else just changes the colors
    colSet = $.extend(defaultColorSet(), colSet);//remove null values just in case
    headerSelect.val(colSet.headerHex);
    titleSelect.val(colSet.titleHex);
    mainBodySelect.val(colSet.mainHex);
    navSelect.val(colSet.sideHex);
    backgroundSelect.val(colSet.bgHex);

    var colorFun = shouldAnimate ? animateColor : changeColor;

    colorFun("header", colSet.headerHex);
    colorFun("h1", colSet.titleHex);
    colorFun("#mainText", colSet.mainHex);
    colorFun("nav, nav li", colSet.sideHex);
    colorFun("body", colSet.bgHex);
    return true;
  }

  function resetCookie() {
    $.cookie("color", null);
  }

  $("#resetButton").click(function() {
    set(defaultColorSet(), true);
    resetCookie();
  });

  function randomNum(maxVal) {
    return Math.floor(Math.random() * maxVal);
  }

  $("#listRandom").click(function() {
    var randomColors = [];
    var numOptions = selectOptions.length;
    for (var i = 0; i < numOptions; i++) {
      randomColors[i] = selectOptions[randomNum(numOptions)];
    }
    var randCols = new ColorSet(randomColors[0], randomColors[1],
      randomColors[2], randomColors[3],
      randomColors[4]);
    set(randCols, true);
    save();
  });

  $("#expRandom").click(function() {

    function randRGBVal() {
      // FF in hex:
      var maxVal = 255;
      var colHex = randomNum(maxVal).toString(16);
      // pad it with leading 0's if needed
      while (colHex.length < 2) {
        colHex = "0" + colHex;
      }
      return colHex;
    }

    function getColorHex() {
      var red = randRGBVal();
      var green = randRGBVal();
      var blue = randRGBVal();
      return "#" + red + green + blue;
    }
    var newCols = [];
    $("select").each(function(index) {
      var col = getColorHex().toUpperCase();
      var self = $(this);
      var newColOption = $("<option>", {
        value : col,
          html : col});
      self.append(newColOption);
      self.val(col);
      newCols[index] = col;
    });
    set(new ColorSet(newCols[0], newCols[1],
          newCols[2], newCols[3],
          newCols[4]), true);
    save();
  });

  function setColorsFromCookie() {
    // reads color and sets the colors accordingly
    // does NOT animate the color change, as the page should
    // load with the changed colors, and the user should not
    // see the original ones (they have already seen an
    // animation)
    set(JSON.parse($.cookie("color")), false);
  }

  setColorsFromCookie();

});
<|endoftext|>


<commit_before>Lail.PaginatedList = function(list, options) {
  // todo: paste John Resig's code

  options = options || {};
  var self = this;

  this.set = [];
  this.set_length = 0;
  this.current_page = 1;
  this.page_size = options.page_size || 20;
  this.page_count = 1;
  this.observer = new Observer();
  this.pagination_container = null;

  this.count = function() {
    return self.set_length;
  }

  this.init = function(list, initialPage) {
    self.set = list;
    self.set_length = self.set.length;
    self.page_count = parseInt(Math.ceil(self.set_length / self.page_size));
    if(self.page_count < 1) {
      self.page_count = 1;
    }
    self.current_page = 0;
    self.gotoPage(initialPage || 1);
  }

  this.gotoPage = function(page_number) {
    page_number = +page_number; // needs to be an integer
    if(page_number < 1) page_number = 1;
    if(page_number > self.total_pages) page_number = self.total_pages;
    if(page_number != self.current_page) __setCurrentPage(page_number);
  }

  function __setCurrentPage(page_number) {
    self.current_page = page_number;
    self.current_set = self.set.slice(self.firstItemIndex(), self.lastItemIndex());
    renderPagination();
    notifyOfPageChange();
  }

  this.firstItemIndex = function() {
    return (self.current_page - 1) * self.page_size;
  }

  this.lastItemIndex = function() {
    var end = self.current_page * self.page_size;
    return (end > self.set_length) ? self.set_length : end;
  }

  this.getCurrentPage = function() {
    return self.current_page;
  }

  this.getCurrentSet = function() {
    return self.current_set;
  }

  this.getEntireSet = function() {
    return self.set;
  }

  function renderPagination() {
    if(self.pagination_container) {
      self.pagination_container.html(self.renderPagination());
    }
  }

  function notifyOfPageChange() {
    self.observer.fire('changed');
  }

  this.onPageChange = function(callback) {
    self.observer.observe('changed', callback);
  }

  this.gotoNextPage = function() {
    if(self.isLastPage()) {
      return false;
    } else {
      self.gotoPage(self.current_page + 1);
      return true;
    }
  }

  this.getNextPageNumber = function() {
    return self.isLastPage() ? self.current_page : (self.current_page + 1);
  }

  this.isLastPage = function() {
    return (self.current_page == self.page_count);
  }

  this.gotoPreviousPage = function() {
    if(self.isFirstPage()) {
      return false;
    } else {
      self.gotoPage(self.current_page - 1);
      return true;
    }
  }

  this.getPreviousPageNumber = function() {
    return self.isFirstPage() ? self.current_page : (self.current_page - 1);
  }

  this.isFirstPage = function() {
    return (self.current_page == 1);
  }

  // !nb: uses jQuery!!!
  this.renderPaginationIn = function(selector) {
    self.pagination_container = jQuery(selector);
    self.pagination_container.delegate('a', 'click', function(e) {
      e.stopImmediatePropagation();
      var a = jQuery(this);
      if(a.hasClass('prev_page')) {
        self.gotoPreviousPage();
      } else if(a.hasClass('next_page')) {
        self.gotoNextPage();
      } else if(a.hasClass('goto_page')) {
        self.gotoPage(parseInt(a.attr('id').substring(5)));
      }
      return false;
    });
  }

  // !todo: use Handlebars
  this.renderPagination = function() {
    var html = '';

    if(self.isFirstPage()) {
      html += '« Previous ';
    } else {
      html += '« Previous ';
    }

    // list no more than 7 page numbers
    var current = self.current_page,
        count = self.page_count,
        min = 1,
        max = count;
    if(self.page_count > 7) {
      min = current - 3;
      max = current + 3;
      var shift = (min < 1) ? (1 - min) : ((max > count) ? (count - max) : 0);
      min += shift;
      max += shift;
    }
    for(var i=min; i<=max; i++) {
      if(i == self.current_page) {
        html += ' ' + i + ' ';
      } else {
        html += ' ' + i + ' ';
      }
    }

    if(self.isLastPage()) {
      html += ' Next »';
    } else {
      html += ' Next »';
    }

    return html;
  }

  // !todo: use handlebars
  this.renderExtendedPagination = function() {
    var html = 'Listing ';
    if(self.count() > 0) {
      html += (self.firstItemIndex()+1) + '–' + self.lastItemIndex() + ' of ';
    }
    return html + self.count() + '';
  }

  this.init(list || []);
}<commit_msg>if only including paginated_list, define Lail<commit_after>var Lail; if(!Lail) Lail={};
Lail.PaginatedList = function(list, options) {
  // todo: paste John Resig's code

  options = options || {};
  var self = this;

  this.set = [];
  this.set_length = 0;
  this.current_page = 1;
  this.page_size = options.page_size || 20;
  this.page_count = 1;
  this.observer = new Observer();
  this.pagination_container = null;

  this.count = function() {
    return self.set_length;
  }

  this.init = function(list, initialPage) {
    self.set = list;
    self.set_length = self.set.length;
    self.page_count = parseInt(Math.ceil(self.set_length / self.page_size));
    if(self.page_count < 1) {
      self.page_count = 1;
    }
    self.current_page = 0;
    self.gotoPage(initialPage || 1);
  }

  this.gotoPage = function(page_number) {
    page_number = +page_number; // needs to be an integer
    if(page_number < 1) page_number = 1;
    if(page_number > self.total_pages) page_number = self.total_pages;
    if(page_number != self.current_page) __setCurrentPage(page_number);
  }

  function __setCurrentPage(page_number) {
    self.current_page = page_number;
    self.current_set = self.set.slice(self.firstItemIndex(), self.lastItemIndex());
    renderPagination();
    notifyOfPageChange();
  }

  this.firstItemIndex = function() {
    return (self.current_page - 1) * self.page_size;
  }

  this.lastItemIndex = function() {
    var end = self.current_page * self.page_size;
    return (end > self.set_length) ? self.set_length : end;
  }

  this.getCurrentPage = function() {
    return self.current_page;
  }

  this.getCurrentSet = function() {
    return self.current_set;
  }

  this.getEntireSet = function() {
    return self.set;
  }

  function renderPagination() {
    if(self.pagination_container) {
      self.pagination_container.html(self.renderPagination());
    }
  }

  function notifyOfPageChange() {
    self.observer.fire('changed');
  }

  this.onPageChange = function(callback) {
    self.observer.observe('changed', callback);
  }

  this.gotoNextPage = function() {
    if(self.isLastPage()) {
      return false;
    } else {
      self.gotoPage(self.current_page + 1);
      return true;
    }
  }

  this.getNextPageNumber = function() {
    return self.isLastPage() ? self.current_page : (self.current_page + 1);
  }

  this.isLastPage = function() {
    return (self.current_page == self.page_count);
  }

  this.gotoPreviousPage = function() {
    if(self.isFirstPage()) {
      return false;
    } else {
      self.gotoPage(self.current_page - 1);
      return true;
    }
  }

  this.getPreviousPageNumber = function() {
    return self.isFirstPage() ? self.current_page : (self.current_page - 1);
  }

  this.isFirstPage = function() {
    return (self.current_page == 1);
  }

  // !nb: uses jQuery!!!
  this.renderPaginationIn = function(selector) {
    self.pagination_container = jQuery(selector);
    self.pagination_container.delegate('a', 'click', function(e) {
      e.stopImmediatePropagation();
      var a = jQuery(this);
      if(a.hasClass('prev_page')) {
        self.gotoPreviousPage();
      } else if(a.hasClass('next_page')) {
        self.gotoNextPage();
      } else if(a.hasClass('goto_page')) {
        self.gotoPage(parseInt(a.attr('id').substring(5)));
      }
      return false;
    });
  }

  // !todo: use Handlebars
  this.renderPagination = function() {
    var html = '';

    if(self.isFirstPage()) {
      html += '« Previous ';
    } else {
      html += '« Previous ';
    }

    // list no more than 7 page numbers
    var current = self.current_page,
        count = self.page_count,
        min = 1,
        max = count;
    if(self.page_count > 7) {
      min = current - 3;
      max = current + 3;
      var shift = (min < 1) ? (1 - min) : ((max > count) ? (count - max) : 0);
      min += shift;
      max += shift;
    }
    for(var i=min; i<=max; i++) {
      if(i == self.current_page) {
        html += ' ' + i + ' ';
      } else {
        html += ' ' + i + ' ';
      }
    }

    if(self.isLastPage()) {
      html += ' Next »';
    } else {
      html += ' Next »';
    }

    return html;
  }

  // !todo: use handlebars
  this.renderExtendedPagination = function() {
    var html = 'Listing ';
    if(self.count() > 0) {
      html += (self.firstItemIndex()+1) + '–' + self.lastItemIndex() + ' of ';
    }
    return html + self.count() + '';
  }

  this.init(list || []);
}<|endoftext|>


<commit_before>const React = require('react')
const prettyBytes = require('prettier-bytes')

const TorrentSummary = require('../lib/torrent-summary')
const TorrentPlayer = require('../lib/torrent-player')
const {dispatcher} = require('../lib/dispatcher')

module.exports = class TorrentList extends React.Component {
  render () {
    var state = this.props.state

    var contents = []
    if (state.downloadPathStatus === 'missing') {
      contents.push(

Download path missing: {state.saved.prefs.downloadPath}

Check that all drives are connected?

Alternatively, choose a new download path
            in Preferences

      )
    }
    var torrentElems = state.saved.torrents.map(
      (torrentSummary) => this.renderTorrent(torrentSummary)
    )
    contents.push(...torrentElems)
    contents.push(

        Drop a torrent file here or paste a magnet link

    )

    return (

        {contents}

    )
  }

  renderTorrent (torrentSummary) {
    var state = this.props.state
    var infoHash = torrentSummary.infoHash
    var isSelected = infoHash && state.selectedInfoHash === infoHash

    // Background image: show some nice visuals, like a frame from the movie, if possible
    var style = {}
    if (torrentSummary.posterFileName) {
      var gradient = isSelected
        ? 'linear-gradient(to bottom, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.4) 100%)'
        : 'linear-gradient(to bottom, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 100%)'
      var posterPath = TorrentSummary.getPosterPath(torrentSummary)
      style.backgroundImage = gradient + `, url('${posterPath}')`
    }

    // Foreground: name of the torrent, basic info like size, play button,
    // cast buttons if available, and delete
    var classes = ['torrent']
    //  playStatus turns the play button into a loading spinner or error icon
    if (torrentSummary.playStatus) classes.push(torrentSummary.playStatus)
    if (isSelected) classes.push('selected')
    if (!infoHash) classes.push('disabled')
    if (!torrentSummary.torrentKey) throw new Error('Missing torrentKey')
    return (

        {this.renderTorrentMetadata(torrentSummary)}
        {infoHash ? this.renderTorrentButtons(torrentSummary) : null}
        {isSelected ? this.renderTorrentDetails(torrentSummary) : null}

    )
  }

  // Show name, download status, % complete
  renderTorrentMetadata (torrentSummary) {
    var name = torrentSummary.name || 'Loading torrent...'
    var elements = [(

{name}

    )]

    // If it's downloading/seeding then show progress info
    var prog = torrentSummary.progress
    if (torrentSummary.error) {
      elements.push(

          {getErrorMessage(torrentSummary)}

      )
    } else if (torrentSummary.status !== 'paused' && prog) {
      elements.push(

          {renderPercentProgress()}
          {renderTotalProgress()}
          {renderPeers()}
          {renderDownloadSpeed()}
          {renderUploadSpeed()}
          {renderEta()}

      )
    }

    return (
{elements}
)

    function renderPercentProgress () {
      var progress = Math.floor(100 * prog.progress)
      return ({progress}%)
    }

    function renderTotalProgress () {
      var downloaded = prettyBytes(prog.downloaded)
      var total = prettyBytes(prog.length || 0)
      if (downloaded === total) {
        return ({downloaded})
      } else {
        return ({downloaded} / {total})
      }
    }

    function renderPeers () {
      if (prog.numPeers === 0) return
      var count = prog.numPeers === 1 ? 'peer' : 'peers'
      return ({prog.numPeers} {count})
    }

    function renderDownloadSpeed () {
      if (prog.downloadSpeed === 0) return
      return (↓ {prettyBytes(prog.downloadSpeed)}/s)
    }

    function renderUploadSpeed () {
      if (prog.uploadSpeed === 0) return
      return (↑ {prettyBytes(prog.uploadSpeed)}/s)
    }

    function renderEta () {
      var downloaded = prog.downloaded
      var total = prog.length || 0
      var missing = total - downloaded
      var downloadSpeed = prog.downloadSpeed
      if (downloadSpeed === 0 || missing === 0) return

      var rawEta = missing / downloadSpeed
      var hours = Math.floor(rawEta / 3600) % 24
      var minutes = Math.floor(rawEta / 60) % 60
      var seconds = Math.floor(rawEta % 60)

      // Only display hours and minutes if they are greater than 0 but always
      // display minutes if hours is being displayed
      var hoursStr = hours ? hours + 'h' : ''
      var minutesStr = (hours || minutes) ? minutes + 'm' : ''
      var secondsStr = seconds + 's'

      return (ETA: {hoursStr} {minutesStr} {secondsStr})
    }
  }

  // Download button toggles between torrenting (DL/seed) and paused
  // Play button starts streaming the torrent immediately, unpausing if needed
  renderTorrentButtons (torrentSummary) {
    var infoHash = torrentSummary.infoHash

    var playIcon, playTooltip, playClass
    if (torrentSummary.playStatus === 'timeout') {
      playIcon = 'warning'
      playTooltip = 'Playback timed out. No seeds? No internet? Click to try again.'
    } else {
      playIcon = 'play_arrow'
      playTooltip = 'Start streaming'
    }

    var downloadIcon, downloadTooltip
    if (torrentSummary.status === 'seeding') {
      downloadIcon = 'file_upload'
      downloadTooltip = 'Seeding. Click to stop.'
    } else if (torrentSummary.status === 'downloading') {
      downloadIcon = 'file_download'
      downloadTooltip = 'Torrenting. Click to stop.'
    } else {
      downloadIcon = 'file_download'
      downloadTooltip = 'Click to start torrenting.'
    }

    // Only show the play/dowload buttons for torrents that contain playable media
    var playButton, downloadButton, positionElem
    if (!torrentSummary.error) {
      downloadButton = (

          {downloadIcon}

      )

      if (TorrentPlayer.isPlayableTorrentSummary(torrentSummary)) {
        playButton = (

            {playIcon}

        )
      }

      // Do we have a saved position? Show it using a radial progress bar on top
      // of the play button, unless already showing a spinner there:
      var willShowSpinner = torrentSummary.playStatus === 'requested'
      var defaultFile = torrentSummary.files &&
        torrentSummary.files[torrentSummary.defaultPlayFileIndex]
      if (defaultFile && defaultFile.currentTime && !willShowSpinner) {
        var fraction = defaultFile.currentTime / defaultFile.duration
        positionElem = this.renderRadialProgressBar(fraction, 'radial-progress-large')
        playClass = 'resume-position'
      }
    }

    return (

        {positionElem}
        {playButton}
        {downloadButton}

          close

    )
  }

  // Show files, per-file download status and play buttons, and so on
  renderTorrentDetails (torrentSummary) {
    var filesElement
    if (torrentSummary.error || !torrentSummary.files) {
      var message = ''
      if (torrentSummary.error === 'path-missing') {
        // Special case error: this torrent's download dir or file is missing
        message = 'Missing path: ' + TorrentSummary.getFileOrFolder(torrentSummary)
      } else if (torrentSummary.error) {
        // General error for this torrent: just show the message
        message = torrentSummary.error.message || torrentSummary.error
      } else if (torrentSummary.status === 'paused') {
        // No file info, no infohash, and we're not trying to download from the DHT
        message = 'Failed to load torrent info. Click the download button to try again...'
      } else {
        // No file info, no infohash, trying to load from the DHT
        message = 'Downloading torrent info...'
      }
      filesElement = (

          {message}

      )
    } else {
      // We do know the files. List them and show download stats for each one
      var fileRows = torrentSummary.files
        .filter((file) => !file.path.includes('/.____padding_file/'))
        .map((file, index) => ({ file, index }))
        .sort(function (a, b) {
          if (a.file.name < b.file.name) return -1
          if (b.file.name  this.renderFileRow(torrentSummary, object.file, object.index))

      filesElement = (

              {fileRows}

      )
    }

    return (

        {filesElement}

    )
  }

  // Show a single torrentSummary file in the details view for a single torrent
  renderFileRow (torrentSummary, file, index) {
    // First, find out how much of the file we've downloaded
    // Are we even torrenting it?
    var isSelected = torrentSummary.selections && torrentSummary.selections[index]
    var isDone = false // Are we finished torrenting it?
    var progress = ''
    if (torrentSummary.progress && torrentSummary.progress.files &&
        torrentSummary.progress.files[index]) {
      var fileProg = torrentSummary.progress.files[index]
      isDone = fileProg.numPiecesPresent === fileProg.numPieces
      progress = Math.round(100 * fileProg.numPiecesPresent / fileProg.numPieces) + '%'
    }

    // Second, for media files where we saved our position, show how far we got
    var positionElem
    if (file.currentTime) {
      // Radial progress bar. 0% = start from 0:00, 270% = 3/4 of the way thru
      positionElem = this.renderRadialProgressBar(file.currentTime / file.duration)
    }

    // Finally, render the file as a table row
    var isPlayable = TorrentPlayer.isPlayable(file)
    var infoHash = torrentSummary.infoHash
    var icon
    var handleClick
    if (isPlayable) {
      icon = 'play_arrow' /* playable? add option to play */
      handleClick = dispatcher('playFile', infoHash, index)
    } else {
      icon = 'description' /* file icon, opens in OS default app */
      handleClick = isDone
        ? dispatcher('openItem', infoHash, index)
        : (e) => e.stopPropagation() // noop if file is not ready
    }
    // TODO: add a css 'disabled' class to indicate that a file cannot be opened/streamed
    var rowClass = ''
    if (!isSelected) rowClass = 'disabled' // File deselected, not being torrented
    if (!isDone && !isPlayable) rowClass = 'disabled' // Can't open yet, can't stream
    return (

          {positionElem}
          {icon}

          {file.name}

          {isSelected ? progress : ''}

          {prettyBytes(file.length)}

          {isSelected ? 'close' : 'add'}

    )
  }

  renderRadialProgressBar (fraction, cssClass) {
    var rotation = 360 * fraction
    var transformFill = {transform: 'rotate(' + (rotation / 2) + 'deg)'}
    var transformFix = {transform: 'rotate(' + rotation + 'deg)'}

    return (

    )
  }
}

function getErrorMessage (torrentSummary) {
  var err = torrentSummary.error
  if (err === 'path-missing') {
    return (

        Path missing.

        Fix and restart the app, or delete the torrent.

    )
  }
  return 'Error'
}
<commit_msg>Fixes for PR #825<commit_after>const React = require('react')
const prettyBytes = require('prettier-bytes')

const TorrentSummary = require('../lib/torrent-summary')
const TorrentPlayer = require('../lib/torrent-player')
const {dispatcher} = require('../lib/dispatcher')

module.exports = class TorrentList extends React.Component {
  render () {
    var state = this.props.state

    var contents = []
    if (state.downloadPathStatus === 'missing') {
      contents.push(

Download path missing: {state.saved.prefs.downloadPath}

Check that all drives are connected?

Alternatively, choose a new download path
            in Preferences

      )
    }
    var torrentElems = state.saved.torrents.map(
      (torrentSummary) => this.renderTorrent(torrentSummary)
    )
    contents.push(...torrentElems)
    contents.push(

        Drop a torrent file here or paste a magnet link

    )

    return (

        {contents}

    )
  }

  renderTorrent (torrentSummary) {
    var state = this.props.state
    var infoHash = torrentSummary.infoHash
    var isSelected = infoHash && state.selectedInfoHash === infoHash

    // Background image: show some nice visuals, like a frame from the movie, if possible
    var style = {}
    if (torrentSummary.posterFileName) {
      var gradient = isSelected
        ? 'linear-gradient(to bottom, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.4) 100%)'
        : 'linear-gradient(to bottom, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 100%)'
      var posterPath = TorrentSummary.getPosterPath(torrentSummary)
      style.backgroundImage = gradient + `, url('${posterPath}')`
    }

    // Foreground: name of the torrent, basic info like size, play button,
    // cast buttons if available, and delete
    var classes = ['torrent']
    //  playStatus turns the play button into a loading spinner or error icon
    if (torrentSummary.playStatus) classes.push(torrentSummary.playStatus)
    if (isSelected) classes.push('selected')
    if (!infoHash) classes.push('disabled')
    if (!torrentSummary.torrentKey) throw new Error('Missing torrentKey')
    return (

        {this.renderTorrentMetadata(torrentSummary)}
        {infoHash ? this.renderTorrentButtons(torrentSummary) : null}
        {isSelected ? this.renderTorrentDetails(torrentSummary) : null}

    )
  }

  // Show name, download status, % complete
  renderTorrentMetadata (torrentSummary) {
    var name = torrentSummary.name || 'Loading torrent...'
    var elements = [(

{name}

    )]

    // If it's downloading/seeding then show progress info
    var prog = torrentSummary.progress
    if (torrentSummary.error) {
      elements.push(

          {getErrorMessage(torrentSummary)}

      )
    } else if (torrentSummary.status !== 'paused' && prog) {
      elements.push(

          {renderPercentProgress()}
          {renderTotalProgress()}
          {renderPeers()}
          {renderDownloadSpeed()}
          {renderUploadSpeed()}
          {renderEta()}

      )
    }

    return (
{elements}
)

    function renderPercentProgress () {
      var progress = Math.floor(100 * prog.progress)
      return ({progress}%)
    }

    function renderTotalProgress () {
      var downloaded = prettyBytes(prog.downloaded)
      var total = prettyBytes(prog.length || 0)
      if (downloaded === total) {
        return ({downloaded})
      } else {
        return ({downloaded} / {total})
      }
    }

    function renderPeers () {
      if (prog.numPeers === 0) return
      var count = prog.numPeers === 1 ? 'peer' : 'peers'
      return ({prog.numPeers} {count})
    }

    function renderDownloadSpeed () {
      if (prog.downloadSpeed === 0) return
      return (↓ {prettyBytes(prog.downloadSpeed)}/s)
    }

    function renderUploadSpeed () {
      if (prog.uploadSpeed === 0) return
      return (↑ {prettyBytes(prog.uploadSpeed)}/s)
    }

    function renderEta () {
      var downloaded = prog.downloaded
      var total = prog.length || 0
      var missing = total - downloaded
      var downloadSpeed = prog.downloadSpeed
      if (downloadSpeed === 0 || missing === 0) return

      var rawEta = missing / downloadSpeed
      var hours = Math.floor(rawEta / 3600) % 24
      var minutes = Math.floor(rawEta / 60) % 60
      var seconds = Math.floor(rawEta % 60)

      // Only display hours and minutes if they are greater than 0 but always
      // display minutes if hours is being displayed
      var hoursStr = hours ? hours + 'h' : ''
      var minutesStr = (hours || minutes) ? minutes + 'm' : ''
      var secondsStr = seconds + 's'

      return (ETA: {hoursStr} {minutesStr} {secondsStr})
    }
  }

  // Download button toggles between torrenting (DL/seed) and paused
  // Play button starts streaming the torrent immediately, unpausing if needed
  renderTorrentButtons (torrentSummary) {
    var infoHash = torrentSummary.infoHash

    var playIcon, playTooltip, playClass
    if (torrentSummary.playStatus === 'timeout') {
      playIcon = 'warning'
      playTooltip = 'Playback timed out. No seeds? No internet? Click to try again.'
    } else {
      playIcon = 'play_arrow'
      playTooltip = 'Start streaming'
    }

    var downloadIcon, downloadTooltip
    if (torrentSummary.status === 'seeding') {
      downloadIcon = 'file_upload'
      downloadTooltip = 'Seeding. Click to stop.'
    } else if (torrentSummary.status === 'downloading') {
      downloadIcon = 'file_download'
      downloadTooltip = 'Torrenting. Click to stop.'
    } else {
      downloadIcon = 'file_download'
      downloadTooltip = 'Click to start torrenting.'
    }

    // Only show the play/dowload buttons for torrents that contain playable media
    var playButton, downloadButton, positionElem
    if (!torrentSummary.error) {
      downloadButton = (

          {downloadIcon}

      )

      // Do we have a saved position? Show it using a radial progress bar on top
      // of the play button, unless already showing a spinner there:
      var willShowSpinner = torrentSummary.playStatus === 'requested'
      var defaultFile = torrentSummary.files &&
        torrentSummary.files[torrentSummary.defaultPlayFileIndex]
      if (defaultFile && defaultFile.currentTime && !willShowSpinner) {
        var fraction = defaultFile.currentTime / defaultFile.duration
        positionElem = this.renderRadialProgressBar(fraction, 'radial-progress-large')
        playClass = 'resume-position'
      }

      if (TorrentPlayer.isPlayableTorrentSummary(torrentSummary)) {
        playButton = (

            {playIcon}

        )
      }
    }

    return (

        {positionElem}
        {playButton}
        {downloadButton}

          close

    )
  }

  // Show files, per-file download status and play buttons, and so on
  renderTorrentDetails (torrentSummary) {
    var filesElement
    if (torrentSummary.error || !torrentSummary.files) {
      var message = ''
      if (torrentSummary.error === 'path-missing') {
        // Special case error: this torrent's download dir or file is missing
        message = 'Missing path: ' + TorrentSummary.getFileOrFolder(torrentSummary)
      } else if (torrentSummary.error) {
        // General error for this torrent: just show the message
        message = torrentSummary.error.message || torrentSummary.error
      } else if (torrentSummary.status === 'paused') {
        // No file info, no infohash, and we're not trying to download from the DHT
        message = 'Failed to load torrent info. Click the download button to try again...'
      } else {
        // No file info, no infohash, trying to load from the DHT
        message = 'Downloading torrent info...'
      }
      filesElement = (

          {message}

      )
    } else {
      // We do know the files. List them and show download stats for each one
      var fileRows = torrentSummary.files
        .filter((file) => !file.path.includes('/.____padding_file/'))
        .map((file, index) => ({ file, index }))
        .sort(function (a, b) {
          if (a.file.name < b.file.name) return -1
          if (b.file.name  this.renderFileRow(torrentSummary, object.file, object.index))

      filesElement = (

              {fileRows}

      )
    }

    return (

        {filesElement}

    )
  }

  // Show a single torrentSummary file in the details view for a single torrent
  renderFileRow (torrentSummary, file, index) {
    // First, find out how much of the file we've downloaded
    // Are we even torrenting it?
    var isSelected = torrentSummary.selections && torrentSummary.selections[index]
    var isDone = false // Are we finished torrenting it?
    var progress = ''
    if (torrentSummary.progress && torrentSummary.progress.files &&
        torrentSummary.progress.files[index]) {
      var fileProg = torrentSummary.progress.files[index]
      isDone = fileProg.numPiecesPresent === fileProg.numPieces
      progress = Math.round(100 * fileProg.numPiecesPresent / fileProg.numPieces) + '%'
    }

    // Second, for media files where we saved our position, show how far we got
    var positionElem
    if (file.currentTime) {
      // Radial progress bar. 0% = start from 0:00, 270% = 3/4 of the way thru
      positionElem = this.renderRadialProgressBar(file.currentTime / file.duration)
    }

    // Finally, render the file as a table row
    var isPlayable = TorrentPlayer.isPlayable(file)
    var infoHash = torrentSummary.infoHash
    var icon
    var handleClick
    if (isPlayable) {
      icon = 'play_arrow' /* playable? add option to play */
      handleClick = dispatcher('playFile', infoHash, index)
    } else {
      icon = 'description' /* file icon, opens in OS default app */
      handleClick = isDone
        ? dispatcher('openItem', infoHash, index)
        : (e) => e.stopPropagation() // noop if file is not ready
    }
    // TODO: add a css 'disabled' class to indicate that a file cannot be opened/streamed
    var rowClass = ''
    if (!isSelected) rowClass = 'disabled' // File deselected, not being torrented
    if (!isDone && !isPlayable) rowClass = 'disabled' // Can't open yet, can't stream
    return (

          {positionElem}
          {icon}

          {file.name}

          {isSelected ? progress : ''}

          {prettyBytes(file.length)}

          {isSelected ? 'close' : 'add'}

    )
  }

  renderRadialProgressBar (fraction, cssClass) {
    var rotation = 360 * fraction
    var transformFill = {transform: 'rotate(' + (rotation / 2) + 'deg)'}
    var transformFix = {transform: 'rotate(' + rotation + 'deg)'}

    return (

    )
  }
}

function getErrorMessage (torrentSummary) {
  var err = torrentSummary.error
  if (err === 'path-missing') {
    return (

        Path missing.

        Fix and restart the app, or delete the torrent.

    )
  }
  return 'Error'
}
<|endoftext|>


<commit_before>'use strict';

var Player = require('./Player');
var PlayScene = require('../scene/play_scene')

var Trial = function Trial(game, round, id) {
  this.id = id;
  this.game = game;
  this.round = round;
  this.active = false;
  this.timer = 6; //in seconds
  this.players = round.players;
  this.loop = null;
  this.winners = [];
};

Trial.prototype.toString = function() {
  return "Trial " + this.id;
}

var timerText;

function updateTimer () {
  function allChosen(players) {
    players.every(function(player) {
      return player.chosen;
    })};
  function resetChosen(players) {
    players.forEach(function(player) {
      player.chosen = false;
    });
  }
  function nextTrial(callback) {
    callback();
  }

  if (allChosen(this.players)) {
    resetChosen(this.players)
  } else if (this.timer <= 0) {
    this.complete();
  } else {
    this.timer -= 1;
  }
  if (this.active) {
    timerText.setText(this.timer);
  }
}

Trial.prototype.start = function() {
  this.players.forEach(function(player) {
    player.addPlate();
  });
  var self = this;
  this.active = true;
  // once the game is started, start the timer
  var style = { font: "65px Arial", fill: "#ff0044"};
  timerText = this.game.add.text(5, 2, this.timer, style);
  this.loop = this.game.time.events.loop(Phaser.Timer.SECOND, _.bind(updateTimer, this, timerText));

  var myTimer = this.game.add.sprite(20, 20, 'timer');
  myTimer.frame = 0;
  myTimer.animations.add('time', [0,1,2,3,4,5,6], 1, true);
  myTimer.animations.play('time');

  this.players.forEach(function(player) {
    var xFromCenter = player.plate.group.x - self.game.width / 2;
    var x = (xFromCenter * .7) + self.game.width / 2;
    self.game.add.tween(player.plate.group).to({x: x, y : self.game.height - 200}, self.timer * 1000, Phaser.Easing.Linear.None, true);
    self.game.add.tween(player.plate.group.scale).to({x: .7, y: .7}, self.timer * 1000, Phaser.Easing.Linear.None, true);
  });
}

Trial.prototype.complete = function() {
  this.active = false;
  this.plates = this.players.map(function(player) {
    player.plate;
  });

  this.winners = this.players.reduce(function(winners, player, idx, players) {
    var score = player.plate.score();
    var highScore = winners[0];
    if (score > highScore) {
      return [score, [player]];
    } else if (score == highScore) {
      return [highScore, winners[1].concat(player)];
    }
    return winners;
  }, [Number.NEGATIVE_INFINITY, []])[1];

  // Cleanup that timer loop we had.
  this.game.time.events.remove(this.loop);
  timerText.destroy();

  this.players.forEach(function(player) {
    player.completePlate();
  });
  // Bubble up to round.
  this.round.nextTrial();
}

module.exports = Trial;
<commit_msg>Timer<commit_after>'use strict';

var Player = require('./Player');
var PlayScene = require('../scene/play_scene')

var Trial = function Trial(game, round, id) {
  this.id = id;
  this.game = game;
  this.round = round;
  this.active = false;
  this.timer = 6; //in seconds
  this.players = round.players;
  this.loop = null;
  this.winners = [];
};

Trial.prototype.toString = function() {
  return "Trial " + this.id;
}

var timerText;

function updateTimer () {
  function allChosen(players) {
    players.every(function(player) {
      return player.chosen;
    })};
  function resetChosen(players) {
    players.forEach(function(player) {
      player.chosen = false;
    });
  }
  function nextTrial(callback) {
    callback();
  }

  if (allChosen(this.players)) {
    resetChosen(this.players)
  } else if (this.timer <= 0) {
    this.complete();
  } else {
    this.timer -= 1;
  }
  if (this.active) {
    timerText.setText(this.timer);
  }
}

Trial.prototype.start = function() {
  this.players.forEach(function(player) {
    player.addPlate();
  });
  var self = this;
  this.active = true;
  // once the game is started, start the timer
  var style = { font: "65px Arial", fill: "#ff0044"};
  timerText = this.game.add.text(5, 2, this.timer, style);
  timerText.visible = false;
  this.loop = this.game.time.events.loop(Phaser.Timer.SECOND, _.bind(updateTimer, this, timerText));

  var myTimer = this.game.add.sprite(20, 20, 'timer');
  myTimer.frame = 0;
  myTimer.scale = {"x": .5, "y": .5};
  myTimer.animations.add('time', [0,1,2,3,4,5,6], 1, true);
  myTimer.animations.play('time');

  this.players.forEach(function(player) {
    var xFromCenter = player.plate.group.x - self.game.width / 2;
    var x = (xFromCenter * .7) + self.game.width / 2;
    self.game.add.tween(player.plate.group).to({x: x, y : self.game.height - 200}, self.timer * 1000, Phaser.Easing.Linear.None, true);
    self.game.add.tween(player.plate.group.scale).to({x: .7, y: .7}, self.timer * 1000, Phaser.Easing.Linear.None, true);
  });
}

Trial.prototype.complete = function() {
  this.active = false;
  this.plates = this.players.map(function(player) {
    player.plate;
  });

  this.winners = this.players.reduce(function(winners, player, idx, players) {
    var score = player.plate.score();
    var highScore = winners[0];
    if (score > highScore) {
      return [score, [player]];
    } else if (score == highScore) {
      return [highScore, winners[1].concat(player)];
    }
    return winners;
  }, [Number.NEGATIVE_INFINITY, []])[1];

  // Cleanup that timer loop we had.
  this.game.time.events.remove(this.loop);
  timerText.destroy();

  this.players.forEach(function(player) {
    player.completePlate();
  });
  // Bubble up to round.
  this.round.nextTrial();
}

module.exports = Trial;
<|endoftext|>