<commit_before>import { get, set, computed } from "@ember/object";
import { scheduleOnce } from "@ember/runloop";
import { default as Service, inject as service } from "@ember/service";
import { vars as varsConfig } from "config";
import { logDebug, logError } from "./logger";
import { Aborted, ExitSignalError, HostingError } from "./errors";
import { clearCache } from "./cache";
import resolvePlayer from "./player/resolve";
import resolveProvider from "./provider/resolve";
import launch from "./launch";
import { setShowInTaskbar, setMinimized, setVisibility } from "nwjs/Window";
import {
	ATTR_GUI_MINIMIZE_MINIMIZE,
	ATTR_GUI_MINIMIZE_TRAY
} from "data/models/settings/gui/fragment";

const { "stream-reload-interval": streamReloadInterval } = varsConfig;

const modelName = "stream";

function setIfNotNull( objA, keyA, objB, keyB ) {
	const val = get( objB, keyB );
	if ( val !== null ) {
		set( objA, keyA, val );
	}
}

export default Service.extend({
	chat: service(),
	modal: service(),
	settings: service(),
	store: service(),

	active: null,
	model: computed(function() {
		const store = get( this, "store" );
		return store.peekAll( modelName );
	}),

	init() {
		this._super( ...arguments );

		// invalidate cache: listen for all settings changes
		// changed properties of model relationships and nested attributes don't trigger isDirty
		get( this, "settings" ).on( "didUpdate", clearCache );
	},

	closeStream( twitchStream ) {
		const model = get( this, "model" );
		const stream = model.findBy( "stream", twitchStream );
		if ( !stream ) { return false; }
		stream.kill();
		return true;
	},

	async startStream( twitchStream, quality ) {
		get( this, "modal" ).openModal( "streaming", this, {
			error: null,
			active: null
		});

		const store = get( this, "store" );
		const channel = get( twitchStream, "channel" );
		const id = get( channel, "name" );
		let stream;

		// is the stream already running?
		if ( store.hasRecordForId( modelName, id ) ) {
			stream = store.recordForId( modelName, id );

			if ( quality !== undefined && get( stream, "quality" ) !== quality ) {
				set( stream, "quality", quality );
			}

			set( this, "active", stream );
			this.onModalOpened( stream );

			return;
		}

		// create a new Stream record
		stream = store.createRecord( modelName, {
			id,
			channel,
			stream: twitchStream,
			quality: get( this, "settings.streaming.quality" ),
			chat_open: get( this, "settings.streams.chat_open" ),
			started: new Date()
		});

		this.onModalOpened( stream );

		// override record with channel specific settings
		await this.getChannelSettings( stream, quality );

		await this.launchStream( stream, true );
	},

	async launchStream( stream, launchChat ) {
		// begin the stream launch procedure
		try {
			set( this, "active", stream );

			await logDebug(
				"Preparing to launch stream",
				() => stream.toJSON({ includeId: true })
			);

			const settingsStreaming = get( this, "settings.streaming" ).toJSON();

			// resolve streaming provider
			const providerObj = await resolveProvider(
				stream,
				settingsStreaming.provider,
				settingsStreaming.providers
			);

			// resolve player
			const playerObj = await resolvePlayer(
				stream,
				settingsStreaming.player,
				settingsStreaming.players
			);

			// launch the stream
			await launch(
				stream,
				providerObj,
				playerObj,
				() => this.onStreamSuccess( stream, launchChat )
			);

		} catch ( error ) {
			await this.onStreamFailure( stream, error );

		} finally {
			await this.onStreamEnd( stream );
		}
	},

	onStreamSuccess( stream, launchChat ) {
		if ( get( stream, "isWatching" ) ) {
			return;
		}
		set( stream, "isWatching", true );

		// setup stream refresh interval
		this.refreshStream( stream );

		// automatically close modal on success
		if ( get( this, "settings.streams.modal_close_launch" ) ) {
			this.closeStreamModal( stream );
		}

		// automatically open chat
		if (
			// do not open chat on stream restarts
			   launchChat
			// require open chat setting
			&& get( stream, "chat_open" )
			&& (
				// context menu not used
				   !get( stream, "strictQuality" )
				// or context menu setting disabled
				|| !get( this, "settings.streams.chat_open_context" )
			)
		) {
			const channel = get( stream, "channel" );
			const chat = get( this, "chat" );
			chat.openChat( channel )
				.catch( () => {} );
		}

		// hide the GUI
		this.minimize( false );
	},

	onStreamFailure( stream, error ) {
		if ( error instanceof Aborted ) {
			return;
		}

		logError( error, () => stream.toJSON({ includeId: true }) );

		// clear cache on error
		if ( !( error instanceof HostingError ) ) {
			clearCache();
		}

		// show error in modal
		set( stream, "error", error );
	},

	onStreamEnd( stream ) {
		if ( get( this, "active" ) === stream ) {
			// close modal of the active stream if it has been enabled in the settings
			if ( get( this, "settings.streams.modal_close_end" ) ) {
				this.closeStreamModal( stream );
			}
		} else if ( !get( stream, "isDeleted" ) ) {
			const error = get( stream, "error" );
			// remove stream from store if modal is not opened
			if ( !error || error instanceof ExitSignalError ) {
				stream.destroyRecord();
			}
		}

		// restore the GUI
		this.minimize( true );
	},

	onModalOpened( stream ) {
		get( this, "modal" ).one( "close", () => {
			// unset the active property on the next tick (ModalDialogComponent.willDestroyElement)
			scheduleOnce( "destroy", () => {
				set( this, "active", null );

				// remove the record from the store
				if ( get( stream, "hasEnded" ) && !get( stream, "isDeleted" ) ) {
					stream.destroyRecord();
				}
			});
		});
	},

	closeStreamModal( stream ) {
		if ( get( this, "active" ) !== stream ) {
			return;
		}

		get( this, "modal" ).closeModal( this );
	},

	async getChannelSettings( stream, quality ) {
		const channel = get( stream, "channel" );
		const channelSettings = await channel.getChannelSettings();

		// override channel specific settings
		if ( quality === undefined ) {
			setIfNotNull( stream, "quality", channelSettings, "streaming_quality" );
			set( stream, "strictQuality", false );
		} else {
			set( stream, "quality", quality );
			set( stream, "strictQuality", true );
		}
		setIfNotNull( stream, "chat_open", channelSettings, "streams_chat_open" );
	},

	killAll() {
		/** @type {Stream[]} */
		const model = get( this, "model" );
		model.slice().forEach( stream => stream.kill() );
	},

	minimize( restore ) {
		switch ( get( this, "settings.gui.minimize" ) ) {
			// minimize
			case ATTR_GUI_MINIMIZE_MINIMIZE:
				setMinimized( !restore );
				break;
			// move to tray: toggle window and taskbar visibility
			case ATTR_GUI_MINIMIZE_TRAY:
				setVisibility( restore );
				if ( get( this, "settings.gui.isVisibleInTaskbar" ) ) {
					setShowInTaskbar( restore );
				}
				break;
		}
	},

	refreshStream( stream ) {
		if ( get( stream, "isDeleted" ) ) {
			return;
		}

		const twitchStream = get( stream, "stream" );
		const reload = () => twitchStream.reload();
		let promise = Promise.reject();

		// try to reload the record at least 3 times
		for ( let i = 0; i < 3; i++ ) {
			promise = promise.catch( reload );
		}

		// queue another refresh
		promise.then( () => {
			setTimeout( () => this.refreshStream( stream ), streamReloadInterval );
		});
	}
});
<commit_msg>Don't auto-close the active stream popup on error<commit_after>import { get, set, computed } from "@ember/object";
import { scheduleOnce } from "@ember/runloop";
import { default as Service, inject as service } from "@ember/service";
import { vars as varsConfig } from "config";
import { logDebug, logError } from "./logger";
import { Aborted, ExitSignalError, HostingError } from "./errors";
import { clearCache } from "./cache";
import resolvePlayer from "./player/resolve";
import resolveProvider from "./provider/resolve";
import launch from "./launch";
import { setShowInTaskbar, setMinimized, setVisibility } from "nwjs/Window";
import {
	ATTR_GUI_MINIMIZE_MINIMIZE,
	ATTR_GUI_MINIMIZE_TRAY
} from "data/models/settings/gui/fragment";

const { "stream-reload-interval": streamReloadInterval } = varsConfig;

const modelName = "stream";

function setIfNotNull( objA, keyA, objB, keyB ) {
	const val = get( objB, keyB );
	if ( val !== null ) {
		set( objA, keyA, val );
	}
}

export default Service.extend({
	chat: service(),
	modal: service(),
	settings: service(),
	store: service(),

	active: null,
	model: computed(function() {
		const store = get( this, "store" );
		return store.peekAll( modelName );
	}),

	init() {
		this._super( ...arguments );

		// invalidate cache: listen for all settings changes
		// changed properties of model relationships and nested attributes don't trigger isDirty
		get( this, "settings" ).on( "didUpdate", clearCache );
	},

	closeStream( twitchStream ) {
		const model = get( this, "model" );
		const stream = model.findBy( "stream", twitchStream );
		if ( !stream ) { return false; }
		stream.kill();
		return true;
	},

	async startStream( twitchStream, quality ) {
		get( this, "modal" ).openModal( "streaming", this, {
			error: null,
			active: null
		});

		const store = get( this, "store" );
		const channel = get( twitchStream, "channel" );
		const id = get( channel, "name" );
		let stream;

		// is the stream already running?
		if ( store.hasRecordForId( modelName, id ) ) {
			stream = store.recordForId( modelName, id );

			if ( quality !== undefined && get( stream, "quality" ) !== quality ) {
				set( stream, "quality", quality );
			}

			set( this, "active", stream );
			this.onModalOpened( stream );

			return;
		}

		// create a new Stream record
		stream = store.createRecord( modelName, {
			id,
			channel,
			stream: twitchStream,
			quality: get( this, "settings.streaming.quality" ),
			chat_open: get( this, "settings.streams.chat_open" ),
			started: new Date()
		});

		this.onModalOpened( stream );

		// override record with channel specific settings
		await this.getChannelSettings( stream, quality );

		await this.launchStream( stream, true );
	},

	async launchStream( stream, launchChat ) {
		// begin the stream launch procedure
		try {
			set( this, "active", stream );

			await logDebug(
				"Preparing to launch stream",
				() => stream.toJSON({ includeId: true })
			);

			const settingsStreaming = get( this, "settings.streaming" ).toJSON();

			// resolve streaming provider
			const providerObj = await resolveProvider(
				stream,
				settingsStreaming.provider,
				settingsStreaming.providers
			);

			// resolve player
			const playerObj = await resolvePlayer(
				stream,
				settingsStreaming.player,
				settingsStreaming.players
			);

			// launch the stream
			await launch(
				stream,
				providerObj,
				playerObj,
				() => this.onStreamSuccess( stream, launchChat )
			);

		} catch ( error ) {
			await this.onStreamFailure( stream, error );

		} finally {
			await this.onStreamEnd( stream );
		}
	},

	onStreamSuccess( stream, launchChat ) {
		if ( get( stream, "isWatching" ) ) {
			return;
		}
		set( stream, "isWatching", true );

		// setup stream refresh interval
		this.refreshStream( stream );

		// automatically close modal on success
		if ( get( this, "settings.streams.modal_close_launch" ) ) {
			this.closeStreamModal( stream );
		}

		// automatically open chat
		if (
			// do not open chat on stream restarts
			   launchChat
			// require open chat setting
			&& get( stream, "chat_open" )
			&& (
				// context menu not used
				   !get( stream, "strictQuality" )
				// or context menu setting disabled
				|| !get( this, "settings.streams.chat_open_context" )
			)
		) {
			const channel = get( stream, "channel" );
			const chat = get( this, "chat" );
			chat.openChat( channel )
				.catch( () => {} );
		}

		// hide the GUI
		this.minimize( false );
	},

	onStreamFailure( stream, error ) {
		if ( error instanceof Aborted ) {
			return;
		}

		logError( error, () => stream.toJSON({ includeId: true }) );

		// clear cache on error
		if ( !( error instanceof HostingError ) ) {
			clearCache();
		}

		// show error in modal
		set( stream, "error", error );
	},

	onStreamEnd( stream ) {
		if ( get( this, "active" ) === stream ) {
			const error = get( stream, "error" );
			// close modal of the active stream if it has been enabled in the settings
			if ( !error && get( this, "settings.streams.modal_close_end" ) ) {
				this.closeStreamModal( stream );
			}
		} else if ( !get( stream, "isDeleted" ) ) {
			const error = get( stream, "error" );
			// remove stream from store if modal is not opened
			if ( !error || error instanceof ExitSignalError ) {
				stream.destroyRecord();
			}
		}

		// restore the GUI
		this.minimize( true );
	},

	onModalOpened( stream ) {
		get( this, "modal" ).one( "close", () => {
			// unset the active property on the next tick (ModalDialogComponent.willDestroyElement)
			scheduleOnce( "destroy", () => {
				set( this, "active", null );

				// remove the record from the store
				if ( get( stream, "hasEnded" ) && !get( stream, "isDeleted" ) ) {
					stream.destroyRecord();
				}
			});
		});
	},

	closeStreamModal( stream ) {
		if ( get( this, "active" ) !== stream ) {
			return;
		}

		get( this, "modal" ).closeModal( this );
	},

	async getChannelSettings( stream, quality ) {
		const channel = get( stream, "channel" );
		const channelSettings = await channel.getChannelSettings();

		// override channel specific settings
		if ( quality === undefined ) {
			setIfNotNull( stream, "quality", channelSettings, "streaming_quality" );
			set( stream, "strictQuality", false );
		} else {
			set( stream, "quality", quality );
			set( stream, "strictQuality", true );
		}
		setIfNotNull( stream, "chat_open", channelSettings, "streams_chat_open" );
	},

	killAll() {
		/** @type {Stream[]} */
		const model = get( this, "model" );
		model.slice().forEach( stream => stream.kill() );
	},

	minimize( restore ) {
		switch ( get( this, "settings.gui.minimize" ) ) {
			// minimize
			case ATTR_GUI_MINIMIZE_MINIMIZE:
				setMinimized( !restore );
				break;
			// move to tray: toggle window and taskbar visibility
			case ATTR_GUI_MINIMIZE_TRAY:
				setVisibility( restore );
				if ( get( this, "settings.gui.isVisibleInTaskbar" ) ) {
					setShowInTaskbar( restore );
				}
				break;
		}
	},

	refreshStream( stream ) {
		if ( get( stream, "isDeleted" ) ) {
			return;
		}

		const twitchStream = get( stream, "stream" );
		const reload = () => twitchStream.reload();
		let promise = Promise.reject();

		// try to reload the record at least 3 times
		for ( let i = 0; i < 3; i++ ) {
			promise = promise.catch( reload );
		}

		// queue another refresh
		promise.then( () => {
			setTimeout( () => this.refreshStream( stream ), streamReloadInterval );
		});
	}
});
<|endoftext|>


<commit_before>'use strict';

var emitter = require('lib/emitter')
var PouchDB = require('pouchdb').default
var $ = require('browserify-zepto')
var AES = require('lib/aes')
var randAvatarIndex = require('lib/avatar').randAvatarIndex
var encrypt = AES.encrypt
var decrypt = AES.decrypt

var db = new PouchDB('cs')
var remote = null
var id = null
var sercret = null

function userID(){
  return id
}

function set(key, value, callback) {
  updateDoc(callback, function(data){
    if(data[key] && value != undefined) {
      $.extend(true, data[key], value)
    } else {
      data[key] = value
    }
  })
}

function updateDoc(callback, processData) {
  if(id == null) return callback(new Error('wallet not ready'));

  db.get(id, function(err, doc){
    var data = JSON.parse(decrypt(doc.data, sercret))
    processData(data)

    doc.data = encrypt(JSON.stringify(data), sercret)
    db.put(doc, callback)

    PouchDB.replicate(db, remote).on('error', function(error) {
      console.error('failed to replicate changes to server', error)
    })
  })
}

function get(key, callback) {
  if(id == null) return callback(new Error('wallet not ready'));

  if(key instanceof Function){
    callback = key
    key = null
  }

  db.get(id, function(err, doc){
    if(err) return callback(err)

    var data = JSON.parse(decrypt(doc.data, sercret))
    var value = data[key]
    if(!key){
      value = data
    }
    callback(null, value)
  })
}

emitter.on('wallet-init', function(data){
  sercret = data.seed
  id = data.id
})

emitter.on('wallet-auth', function(data){
  remote = getRemote(data)

  db.get(id, function(err){
    if(err) {
      if(err.status === 404) {
        return firstTimePull()
      }
      return console.error(err)
    }

    PouchDB.replicate(db, remote).on('complete', function() {
      emitter.emit('db-ready')
    })
  })
})

function getRemote(data){
  var scheme = 'https'
  var url = [
    scheme, "://",
    id, ":", data.token, data.pin,
    "@", process.env.DB_HOST
  ]
  url = url.concat(["/cs", id]).join('')
  return new PouchDB(url)
}

function firstTimePull() {
  PouchDB.replicate(remote, db).on('complete', function() {
    db.get(id, function(err){
      if(err) {
        if(err.status === 404) return initializeRecord();
        return console.error(err)
      }
      emitter.emit('db-ready')
    })
  })
}

function initializeRecord(){
  var defaultValue = {
    systemInfo: { preferredCurrency: 'USD' },
    userInfo: {
      firstName: '',
      lastName: '',
      email: '',
      avatarIndex: randAvatarIndex()
    }
  }

  var doc = {
    _id: id,
    data: encrypt(JSON.stringify(defaultValue), sercret)
  }

  db.put(doc, function(err){
    if(err) return console.error(err);

    emitter.emit('db-ready')
  })
}

module.exports = {
  userID: userID,
  get: get,
  set: set
}

<commit_msg>Speedup db initialization<commit_after>'use strict';

var emitter = require('lib/emitter')
var PouchDB = require('pouchdb').default
var $ = require('browserify-zepto')
var AES = require('lib/aes')
var randAvatarIndex = require('lib/avatar').randAvatarIndex
var encrypt = AES.encrypt
var decrypt = AES.decrypt

var db = new PouchDB('cs')
var remote = null
var id = null
var sercret = null

function userID(){
  return id
}

function set(key, value, callback) {
  updateDoc(callback, function(data){
    if(data[key] && value != undefined) {
      $.extend(true, data[key], value)
    } else {
      data[key] = value
    }
  })
}

function updateDoc(callback, processData) {
  if(id == null) return callback(new Error('wallet not ready'));

  db.get(id, function(err, doc){
    var data = JSON.parse(decrypt(doc.data, sercret))
    processData(data)

    doc.data = encrypt(JSON.stringify(data), sercret)
    db.put(doc, callback)

    PouchDB.replicate(db, remote).on('error', function(error) {
      console.error('failed to replicate changes to server', error)
    })
  })
}

function get(key, callback) {
  if(id == null) return callback(new Error('wallet not ready'));

  if(key instanceof Function){
    callback = key
    key = null
  }

  db.get(id, function(err, doc){
    if(err) return callback(err)

    var data = JSON.parse(decrypt(doc.data, sercret))
    var value = data[key]
    if(!key){
      value = data
    }
    callback(null, value)
  })
}

emitter.on('wallet-init', function(data){
  sercret = data.seed
  id = data.id
})

emitter.on('wallet-auth', function(data){
  remote = getRemote(data)

  db.get(id, function(err){
    if(err) {
      if(err.status === 404) {
        return firstTimePull()
      }
      return console.error(err)
    }

    emitter.emit('db-ready')
    PouchDB.replicate(db, remote).on('error', function(error) {
      console.error('failed to replicate changes to server', error)
    })
  })
})

function getRemote(data){
  var scheme = 'https'
  var url = [
    scheme, "://",
    id, ":", data.token, data.pin,
    "@", process.env.DB_HOST
  ]
  url = url.concat(["/cs", id]).join('')
  return new PouchDB(url)
}

function firstTimePull() {
  PouchDB.replicate(remote, db).on('complete', function() {
    db.get(id, function(err){
      if(err) {
        if(err.status === 404) return initializeRecord();
        return console.error(err)
      }
      emitter.emit('db-ready')
    })
  })
}

function initializeRecord(){
  var defaultValue = {
    systemInfo: { preferredCurrency: 'USD' },
    userInfo: {
      firstName: '',
      lastName: '',
      email: '',
      avatarIndex: randAvatarIndex()
    }
  }

  var doc = {
    _id: id,
    data: encrypt(JSON.stringify(defaultValue), sercret)
  }

  db.put(doc, function(err){
    if(err) return console.error(err);

    emitter.emit('db-ready')
  })
}

module.exports = {
  userID: userID,
  get: get,
  set: set
}

<|endoftext|>


<commit_before>'use strict';

angular.module('mgcrea.ngStrap.button', ['ngAnimate'])

  .provider('$button', function() {

    var defaults = this.defaults = {
      activeClass:'active',
      toggleEvent:'click'
    };

    this.$get = function() {
      return {defaults: defaults};
    };

  })

  .directive('bsCheckboxGroup', function() {

    return {
      restrict: 'A',
      require: 'ngModel',
      compile: function postLink(element, attr) {
        element.attr('data-toggle', 'buttons');
        element.removeAttr('ng-model');
        var children = element[0].querySelectorAll('input[type="checkbox"]');
        angular.forEach(children, function(child) {
          var childEl = angular.element(child);
          childEl.attr('bs-checkbox', '');
          childEl.attr('ng-model', attr.ngModel + '.' + childEl.attr('value'));
        });
      }

    };

  })

  .directive('bsCheckbox', function($window, $button, $$animateReflow) {

    var defaults = $button.defaults;
    var constantValueRegExp = /^(true|false|\d+)$/;

    return {
      restrict: 'A',
      require: 'ngModel',
      link: function postLink(scope, element, attr, controller) {

        var options = defaults;

        // Support label > input[type="checkbox"]
        var isInput = element[0].nodeName === 'INPUT';
        var activeElement = isInput ? element.parent() : element;

        var trueValue = angular.isDefined(attr.trueValue) ? attr.trueValue : true;
        if(constantValueRegExp.test(attr.trueValue)) {
          trueValue = scope.$eval(attr.trueValue);
        }
        var falseValue = angular.isDefined(attr.falseValue) ? attr.falseValue : false;
        if(constantValueRegExp.test(attr.falseValue)) {
          falseValue = scope.$eval(attr.falseValue);
        }

        // Parse exotic values
        var hasExoticValues = typeof trueValue !== 'boolean' || typeof falseValue !== 'boolean';
        if(hasExoticValues) {
          controller.$parsers.push(function(viewValue) {
            // console.warn('$parser', element.attr('ng-model'), 'viewValue', viewValue);
            return viewValue ? trueValue : falseValue;
          });
          // Fix rendering for exotic values
          scope.$watch(attr.ngModel, function(newValue, oldValue) {
            controller.$render();
          });
        }

        // model -> view
        controller.$render = function () {
          // console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
          var isActive = angular.equals(controller.$modelValue, trueValue);
          $$animateReflow(function() {
            if(isInput) element[0].checked = isActive;
            activeElement.toggleClass(options.activeClass, isActive);
          });
        };

        // view -> model
        element.bind(options.toggleEvent, function() {
          scope.$apply(function () {
            // console.warn('!click', element.attr('ng-model'), 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue, 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue);
            if(!isInput) {
              controller.$setViewValue(!activeElement.hasClass('active'));
            }
            if(!hasExoticValues) {
              controller.$render();
            }
          });
        });

      }

    };

  })

  .directive('bsRadioGroup', function() {

    return {
      restrict: 'A',
      require: 'ngModel',
      compile: function postLink(element, attr) {
        element.attr('data-toggle', 'buttons');
        element.removeAttr('ng-model');
        var children = element[0].querySelectorAll('input[type="radio"]');
        angular.forEach(children, function(child) {
          angular.element(child).attr('bs-radio', '');
          angular.element(child).attr('ng-model', attr.ngModel);
        });
      }

    };

  })

  .directive('bsRadio', function($window, $button, $$animateReflow) {

    var defaults = $button.defaults;
    var constantValueRegExp = /^(true|false|\d+)$/;

    return {
      restrict: 'A',
      require: 'ngModel',
      link: function postLink(scope, element, attr, controller) {

        var options = defaults;

        // Support `label > input[type="radio"]` markup
        var isInput = element[0].nodeName === 'INPUT';
        var activeElement = isInput ? element.parent() : element;

        var value = constantValueRegExp.test(attr.value) ? scope.$eval(attr.value) : attr.value;

        // model -> view
        controller.$render = function () {
          // console.warn('$render', element.attr('value'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
          var isActive = angular.equals(controller.$modelValue, value);
          $$animateReflow(function() {
            if(isInput) element[0].checked = isActive;
            activeElement.toggleClass(options.activeClass, isActive);
          });
        };

        // view -> model
        element.bind(options.toggleEvent, function() {
          scope.$apply(function () {
            // console.warn('!click', element.attr('value'), 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue, 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue);
            controller.$setViewValue(value);
            controller.$render();
          });
        });

      }

    };

  });
<commit_msg>chore(button): remove useless $window dep<commit_after>'use strict';

angular.module('mgcrea.ngStrap.button', ['ngAnimate'])

  .provider('$button', function() {

    var defaults = this.defaults = {
      activeClass:'active',
      toggleEvent:'click'
    };

    this.$get = function() {
      return {defaults: defaults};
    };

  })

  .directive('bsCheckboxGroup', function() {

    return {
      restrict: 'A',
      require: 'ngModel',
      compile: function postLink(element, attr) {
        element.attr('data-toggle', 'buttons');
        element.removeAttr('ng-model');
        var children = element[0].querySelectorAll('input[type="checkbox"]');
        angular.forEach(children, function(child) {
          var childEl = angular.element(child);
          childEl.attr('bs-checkbox', '');
          childEl.attr('ng-model', attr.ngModel + '.' + childEl.attr('value'));
        });
      }

    };

  })

  .directive('bsCheckbox', function($button, $$animateReflow) {

    var defaults = $button.defaults;
    var constantValueRegExp = /^(true|false|\d+)$/;

    return {
      restrict: 'A',
      require: 'ngModel',
      link: function postLink(scope, element, attr, controller) {

        var options = defaults;

        // Support label > input[type="checkbox"]
        var isInput = element[0].nodeName === 'INPUT';
        var activeElement = isInput ? element.parent() : element;

        var trueValue = angular.isDefined(attr.trueValue) ? attr.trueValue : true;
        if(constantValueRegExp.test(attr.trueValue)) {
          trueValue = scope.$eval(attr.trueValue);
        }
        var falseValue = angular.isDefined(attr.falseValue) ? attr.falseValue : false;
        if(constantValueRegExp.test(attr.falseValue)) {
          falseValue = scope.$eval(attr.falseValue);
        }

        // Parse exotic values
        var hasExoticValues = typeof trueValue !== 'boolean' || typeof falseValue !== 'boolean';
        if(hasExoticValues) {
          controller.$parsers.push(function(viewValue) {
            // console.warn('$parser', element.attr('ng-model'), 'viewValue', viewValue);
            return viewValue ? trueValue : falseValue;
          });
          // Fix rendering for exotic values
          scope.$watch(attr.ngModel, function(newValue, oldValue) {
            controller.$render();
          });
        }

        // model -> view
        controller.$render = function () {
          // console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
          var isActive = angular.equals(controller.$modelValue, trueValue);
          $$animateReflow(function() {
            if(isInput) element[0].checked = isActive;
            activeElement.toggleClass(options.activeClass, isActive);
          });
        };

        // view -> model
        element.bind(options.toggleEvent, function() {
          scope.$apply(function () {
            // console.warn('!click', element.attr('ng-model'), 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue, 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue);
            if(!isInput) {
              controller.$setViewValue(!activeElement.hasClass('active'));
            }
            if(!hasExoticValues) {
              controller.$render();
            }
          });
        });

      }

    };

  })

  .directive('bsRadioGroup', function() {

    return {
      restrict: 'A',
      require: 'ngModel',
      compile: function postLink(element, attr) {
        element.attr('data-toggle', 'buttons');
        element.removeAttr('ng-model');
        var children = element[0].querySelectorAll('input[type="radio"]');
        angular.forEach(children, function(child) {
          angular.element(child).attr('bs-radio', '');
          angular.element(child).attr('ng-model', attr.ngModel);
        });
      }

    };

  })

  .directive('bsRadio', function($button, $$animateReflow) {

    var defaults = $button.defaults;
    var constantValueRegExp = /^(true|false|\d+)$/;

    return {
      restrict: 'A',
      require: 'ngModel',
      link: function postLink(scope, element, attr, controller) {

        var options = defaults;

        // Support `label > input[type="radio"]` markup
        var isInput = element[0].nodeName === 'INPUT';
        var activeElement = isInput ? element.parent() : element;

        var value = constantValueRegExp.test(attr.value) ? scope.$eval(attr.value) : attr.value;

        // model -> view
        controller.$render = function () {
          // console.warn('$render', element.attr('value'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
          var isActive = angular.equals(controller.$modelValue, value);
          $$animateReflow(function() {
            if(isInput) element[0].checked = isActive;
            activeElement.toggleClass(options.activeClass, isActive);
          });
        };

        // view -> model
        element.bind(options.toggleEvent, function() {
          scope.$apply(function () {
            // console.warn('!click', element.attr('value'), 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue, 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue);
            controller.$setViewValue(value);
            controller.$render();
          });
        });

      }

    };

  });
<|endoftext|>


<commit_before>function start_bust(e) {
    e.preventDefault();
	alert("TODO: bust");
}

function load_sessions() {
	$.getJSON("/bust", null, load_sessions_data);
}

function session_detail_clicked(event) {
	var settings = {
		follow_dirs: {
			label: "Recursively bust directories",
			type: "flag"
		}
	};
	var tbody = $("#detailsModal .detailsParams tbody").empty();
	$.each(settings, function(name, setting) {
		var config = event.data.config;
		var tr = document.createElement("tr");
		var key = document.createElement("td");
		var value = document.createElement("td");
		key.appendChild(document.createTextNode(setting.label));
		value.appendChild(document.createTextNode(config[name] ? "enabled" : "disabled"));
		tr.appendChild(key);
		tr.appendChild(value);
		tbody.append(tr);
	});
	$('#detailsModal').modal({});
}

function load_sessions_data(sessions) {
	var tbody = $("#sessions tbody").empty();
	$(sessions).each(function (n, session) {
		var tr = document.createElement("tr");
		var id = document.createElement("td");
		var url = document.createElement("td");
		var a = document.createElement("a");
		var st = document.createElement("td");
		var details = document.createElement("td");
		var button = document.createElement("a");
		id.className = "bust_id";
		id.appendChild(document.createTextNode(session.id));
		a.appendChild(document.createTextNode(session.url));
		a.href = session.url;
		url.appendChild(a);
		tr.appendChild(id);
		tr.appendChild(url);
		switch (session.status) {
			case "broken":
				tr.className = "danger";
				st.appendChild(document.createTextNode("internal error"));
				break;
			case "running":
				tr.className = "active";
				st.appendChild(document.createTextNode("running"));
				break;
			case "finished":
				tr.className = "success";
				st.appendChild(document.createTextNode("finished"));
				break;
			case "not_started":
				tr.className = "warning";
				st.appendChild(document.createTextNode("waiting to start"));
				break;
		}
		tr.appendChild(st);
		button.className = "btn btn-info btn-xs";
		button.appendChild(document.createTextNode("Details"));
		details.appendChild(button);
		tr.appendChild(details);
		tbody.append(tr);
		$(button).on("click", null, session, session_detail_clicked);
	});
}

$(function() {
    $("#bust").submit(start_bust);
	load_sessions();
});
<commit_msg>seession_detail: display parse_body and follow_redirs<commit_after>function start_bust(e) {
    e.preventDefault();
	alert("TODO: bust");
}

function load_sessions() {
	$.getJSON("/bust", null, load_sessions_data);
}

function session_detail_clicked(event) {
	var settings = {
		parse_body: {
			label: "Parse response body",
			type: "flag"
		},
		follow_redirs: {
			label: "Follow redirections",
			type: "flag"
		},
		follow_dirs: {
			label: "Recursively bust directories",
			type: "flag"
		}
	};
	var tbody = $("#detailsModal .detailsParams tbody").empty();
	$.each(settings, function(name, setting) {
		var config = event.data.config;
		var tr = document.createElement("tr");
		var key = document.createElement("td");
		var value = document.createElement("td");
		key.appendChild(document.createTextNode(setting.label));
		value.appendChild(document.createTextNode(config[name] ? "enabled" : "disabled"));
		tr.appendChild(key);
		tr.appendChild(value);
		tbody.append(tr);
	});
	$('#detailsModal').modal({});
}

function load_sessions_data(sessions) {
	var tbody = $("#sessions tbody").empty();
	$(sessions).each(function (n, session) {
		var tr = document.createElement("tr");
		var id = document.createElement("td");
		var url = document.createElement("td");
		var a = document.createElement("a");
		var st = document.createElement("td");
		var details = document.createElement("td");
		var button = document.createElement("a");
		id.className = "bust_id";
		id.appendChild(document.createTextNode(session.id));
		a.appendChild(document.createTextNode(session.url));
		a.href = session.url;
		url.appendChild(a);
		tr.appendChild(id);
		tr.appendChild(url);
		switch (session.status) {
			case "broken":
				tr.className = "danger";
				st.appendChild(document.createTextNode("internal error"));
				break;
			case "running":
				tr.className = "active";
				st.appendChild(document.createTextNode("running"));
				break;
			case "finished":
				tr.className = "success";
				st.appendChild(document.createTextNode("finished"));
				break;
			case "not_started":
				tr.className = "warning";
				st.appendChild(document.createTextNode("waiting to start"));
				break;
		}
		tr.appendChild(st);
		button.className = "btn btn-info btn-xs";
		button.appendChild(document.createTextNode("Details"));
		details.appendChild(button);
		tr.appendChild(details);
		tbody.append(tr);
		$(button).on("click", null, session, session_detail_clicked);
	});
}

$(function() {
    $("#bust").submit(start_bust);
	load_sessions();
});
<|endoftext|>


<commit_before>pref("extensions.{e4a8a97b-f2ed-450b-b12d-ee082ba24781}.description", "chrome://greasemonkey/locale/greasemonkey.properties");
pref("extensions.greasemonkey.dataIsGreaseable", false);
pref("extensions.greasemonkey.enableScriptRefreshing", true);
pref("extensions.greasemonkey.fileIsGreaseable", false);
pref("extensions.greasemonkey.globalExcludes", '[]');
pref("extensions.greasemonkey.newScript.removeUnused", true);
pref("extensions.greasemonkey.newScript.template", '// ==UserScript==\n// @name        %name%\n// @namespace   %namespace%\n// @description %description%\n// @include     %include%\n// @exclude     %exclude%\n// @version     1\n// @grant       none\n// ==/UserScript==');
pref("extensions.greasemonkey.haveInsertedToolbarbutton", false);
pref("extensions.greasemonkey.installDelay", 5);
pref("extensions.greasemonkey.logChrome", false);
pref("extensions.greasemonkey.requireSecureUpdates", true);
pref("extensions.greasemonkey.showGrantsWarning", true);
pref("extensions.greasemonkey.sniffGrants", false);
pref("extensions.greasemonkey.stats.id", "");
pref("extensions.greasemonkey.stats.interval", 604800000);  // ms = 1 week
pref("extensions.greasemonkey.stats.lastsubmittime", "Thu, 01 Jan 1970 00:00:00 GMT");
pref("extensions.greasemonkey.stats.optedin", false);
pref("extensions.greasemonkey.stats.prompted", false);
pref("extensions.greasemonkey.stats.url", "https://stats.greasespot.net/submit/");
pref("extensions.greasemonkey.sync.enabled", true);
pref("extensions.greasemonkey.sync.values", false);
pref("extensions.greasemonkey.sync.values_max_size_per_script", 65536);
pref("extensions.greasemonkey.unmhtIsGreaseable", false);
pref("extensions.greasemonkey.version", "0.0");
pref("services.sync.prefs.sync.extensions.greasemonkey.enableScriptRefreshing", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.globalExcludes", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.newScript.removeUnused", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.newScript.template", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.requireSecureUpdates", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.showGrantsWarning", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.stats.optedin", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.stats.lastsubmittime", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.sync.enabled", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.sync.values", true);
<commit_msg>Update greasemonkey.js<commit_after>pref("extensions.{e4a8a97b-f2ed-450b-b12d-ee082ba24781}.description", "chrome://greasemonkey/locale/greasemonkey.properties");
pref("extensions.greasemonkey.dataIsGreaseable", false);
pref("extensions.greasemonkey.enableScriptRefreshing", true);
pref("extensions.greasemonkey.fileIsGreaseable", false);
pref("extensions.greasemonkey.globalExcludes", '[]');
pref("extensions.greasemonkey.newScript.removeUnused", true);
pref("extensions.greasemonkey.newScript.template", '// ==UserScript==\n// @name        %name%\n// @namespace   %namespace%\n// @description %description%\n// @include     %include%\n// @exclude     %exclude%\n// @version     1\n// @grant       none\n// ==/UserScript==');
pref("extensions.greasemonkey.haveInsertedToolbarbutton", false);
pref("extensions.greasemonkey.installDelay", 5);
pref("extensions.greasemonkey.logChrome", false);
pref("extensions.greasemonkey.requireSecureUpdates", true);
pref("extensions.greasemonkey.showGrantsWarning", true);
pref("extensions.greasemonkey.sniffGrants", false);
pref("extensions.greasemonkey.sortBy", "uiState,name");
pref("extensions.greasemonkey.stats.id", "");
pref("extensions.greasemonkey.stats.interval", 604800000);  // ms = 1 week
pref("extensions.greasemonkey.stats.lastsubmittime", "Thu, 01 Jan 1970 00:00:00 GMT");
pref("extensions.greasemonkey.stats.optedin", false);
pref("extensions.greasemonkey.stats.prompted", false);
pref("extensions.greasemonkey.stats.url", "https://stats.greasespot.net/submit/");
pref("extensions.greasemonkey.sync.enabled", true);
pref("extensions.greasemonkey.sync.values", false);
pref("extensions.greasemonkey.sync.values_max_size_per_script", 65536);
pref("extensions.greasemonkey.unmhtIsGreaseable", false);
pref("extensions.greasemonkey.version", "0.0");
pref("services.sync.prefs.sync.extensions.greasemonkey.enableScriptRefreshing", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.globalExcludes", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.newScript.removeUnused", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.newScript.template", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.requireSecureUpdates", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.showGrantsWarning", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.stats.optedin", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.stats.lastsubmittime", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.sync.enabled", true);
pref("services.sync.prefs.sync.extensions.greasemonkey.sync.values", true);
<|endoftext|>


<commit_before>'use strict';

var Handlebars = require('handlebars');

String.prototype.startsWith = String.prototype.startsWith || function(prefix) {
	return this.indexOf(prefix) === 0;
};

String.prototype.endsWith = String.prototype.endsWith || function(suffix) {
	return this.indexOf(suffix, this.length - suffix.length) >= 0;
};

module.exports = function(grunt) {
	grunt.registerTask('register_handlebars_helpers', function() {
		Handlebars.registerHelper('link-to', linkTo);
		Handlebars.registerHelper('link-to-class', linkToClass);
		Handlebars.registerHelper('link-to-method', linkToMethod);
		Handlebars.registerHelper('link-to-property', linkToProperty);
		Handlebars.registerHelper('strip-outer-paragraph', stripOuterParagraph);
	});
};

function linkTo(content, href) {
	return new Handlebars.SafeString('' + content + '');
}

function linkToClass(content, name, options) {
	if (!options) {
		options = name;
		name = content;
	}

	return new Handlebars.SafeString('' + content + '');
}

function linkToMethod(content, name, method, options) {
	if (!options) {
		options = method;
		method = name;
		name = content;
		content = method;
	}

	return new Handlebars.SafeString('' + content + '');
}

function linkToProperty(content, name, property, options) {
	if (!options) {
		options = property;
		property = name;
		name = content;
		content = property;
	}

	return new Handlebars.SafeString('' + content + '');
}

function stripOuterParagraph(text) {
	text = text.trim();

	if (text.startsWith('
') && text.endsWith('
')) {
	 text = text.substring(3, text.length - 4);
	}

	return new Handlebars.SafeString(text);
}<commit_msg>More duplicate code removal.<commit_after>'use strict';

var Handlebars = require('handlebars');

String.prototype.startsWith = String.prototype.startsWith || function(prefix) {
	return this.indexOf(prefix) === 0;
};

String.prototype.endsWith = String.prototype.endsWith || function(suffix) {
	return this.indexOf(suffix, this.length - suffix.length) >= 0;
};

module.exports = function(grunt) {
	grunt.registerTask('register_handlebars_helpers', function() {
		Handlebars.registerHelper('link-to', linkTo);
		Handlebars.registerHelper('link-to-class', linkToClass);
		Handlebars.registerHelper('link-to-method', linkToMethod);
		Handlebars.registerHelper('link-to-property', linkToProperty);
		Handlebars.registerHelper('strip-outer-paragraph', stripOuterParagraph);
	});
};

function linkTo(content, href) {
	return new Handlebars.SafeString('' + content + '');
}

function linkToClass(content, name, options) {
	if (!options) {
		options = name;
		name = content;
	}

	return new Handlebars.SafeString('' + content + '');
}

function linkToMethod(linkName, className, methodName, options) {
	return linkToClassItem(linkName, className, methodName, options, 'method');
}

function linkToProperty(linkName, className, propertyName, options) {
	return linkToClassItem(linkName, className, propertyName, options, 'property');
}

function linkToClassItem(linkName, className, itemName, options, type) {
	if (!options) {
		options = itemName;
		itemName = className;
		className = linkName;
		linkName = itemName;
	}

	var html = '' + linkName + '';

	return new Handlebars.SafeString(html);
}

function stripOuterParagraph(text) {
	text = text.trim();

	if (text.startsWith('
') && text.endsWith('
')) {
	 text = text.substring(3, text.length - 4);
	}

	return new Handlebars.SafeString(text);
}<|endoftext|>


<commit_before>var ics = require('./ics-builder');
var fs = require('fs')
    , filename = process.argv[2];
/**
 * Takes in any time and converts it from a 12HR format to the 24HR format for 7:00PM is 19:00 etc
 * @param {String} time
 * @return {String} 24HR formatted time
 */
function convertTo24Hour(time) {
    var hours = parseInt(time.substr(0, 2), 10);
    if(time.indexOf('AM') > -1 && hours == 12) {
        time = time.replace('12', '0');
    }else if(time.indexOf('PM')  > -1 && hours < 12) {
        time = time.replace(hours, (hours + 12));
    }

    return time.replace(/(AM|PM)/, '');
}

/**
 * Takes any date and turns it into the correct format, so javascript gets the right date.
 * MM/DD/YYYY is the format the javascript object reads or YYYY/MM/DD
 * @param {String} startDate
 * @param {String} endDate
 * @return {String Array} [startDate, endDate]  with format MM/DD/YYYY or YYYY/MM/DD or YYYY-MM-DD
 */
function fixDateFormat(sem_start, sem_end) {
    var startDateSplit = sem_start.split("/");
    var endDateSplit = sem_end.split("/");

    var startMonth = parseInt(startDateSplit[0], 10);
    var startDay = parseInt(startDateSplit[1], 10);
    var startYear = parseInt(startDateSplit[2], 10);

    var endMonth = parseInt(endDateSplit[0], 10);
    var endDay = parseInt(endDateSplit[1], 10);
    var endYear = parseInt(endDateSplit[2], 10);

    var dayDiff = endDay - startDay;
    // If it contains the '-' or the first slot is YYYY it is already in correct format
    if (sem_start.indexOf('-') > -1 || startMonth.toString().length === 4) {
        return [sem_start, sem_end]
    }
    // Date is DD/MM/YYYY as month can't be > 12, switch the MM and DD or If the difference is 3 months(Fall/Winter), 1 month(Spring/Summer) or -5 months(multi-semester(Sept-Apr)),
    else if (startMonth > 12 || endMonth > 12 || (startYear === endYear && startMonth > endMonth) ||
    ([1,3,-5].indexOf(dayDiff) > -1 && [1, 5, 7, 9].indexOf(startDay) > -1)) {
        return [[startDay, startMonth, startYear].join("/"),[endDay, endMonth, endYear].join("/")];
    }

    return [sem_start, sem_end]
}

/**
 * Takes in the raw data of timetable and get all the important details including, course name, professor, times etc.
 * @param {String} text
 * @return {Object/JSON} timetable
 */
function parse(text){
    var _wsRegex = /\s+/;
    var _ampm = /:\d{2}[aAPp][mM]/;
    var _courseTitleLookAhead = /(?=\b(?:\w{2,}\ \w{1,5})\ -\ (?:[^\r\n]+)\b)/g;
    var _courseTitleRegex = /(?:\b(\w{2,}\ \w{1,5})\ -\ ([^\r\n]+)\b)/;
    var _dayOfWeekRegex = /([MoTuhWeFrSaS]{2,6})/;
    var _timeRegex  =  _ampm.exec(text) ? /([012]?\d\:[0-5]\d[AP]M)/ : /([012]?\d\:[0-5]\d)/;
    var _timeDayPairRegex = new RegExp(_dayOfWeekRegex.source + _wsRegex.source + _timeRegex.source +  _wsRegex.source + "-" +  _wsRegex.source + _timeRegex.source);
    var _timeDayPairLookAhead =  _ampm.exec(text) ? /(?=\b(?:[MoTuhWeFrSaS]{2,6})\s+(?:[012]?\d\:[0-5]\d[AP]M)\s+-\s+(?:[012]?\d\:[0-5]\d[AP]M)\b)/ : /(?=\b(?:[MoTuhWeFrSaS]{2,6})\s+(?:[012]?\d\:[0-5]\d)\s+-\s+(?:[012]?\d\:[0-5]\d)\b)/;
    var _courseCodeLookAhead = /(?=\b\d{5}\b)/;
    var _timeDayPairOrCourseCodeLookAhead = new RegExp(_courseCodeLookAhead.source + "|" + _timeDayPairLookAhead.source ,"g");
    // Check for any string, location can be "SEE CLASS NOTES" or "TBA" or maybe something else
    var _locationRegex = /(\s[\-\w ,]+)/;
    var _profRegex = /((\b[A-Za-z]+\s)(?:[A-Za-z\-.]+\s?)+|(\b[A-Za-z]+\s))/;
    var _locationProfPair = new RegExp(_locationRegex.source + _wsRegex.source + _profRegex.source);
    var _semesterRegex = /((?:\d{2}\/\d{2}\/\d{4})|(?:\d{4}\/\d{2}\/\d{2})|(?:\d{4}-\d{2}-\d{2}))/;
    var _semesterPairRegex = new RegExp(_semesterRegex.source +  _wsRegex.source + "-" +  _wsRegex.source +  _semesterRegex.source);
    var _sectionTypeRegex = /(\d+)\s+([A-Z]{1}\d+)\s+(?=Lecture|Tutorial|Laboratory)([A-Za-z]{3,}\b)/;
    var _dayRegex = /(Mo)|(Tu)|(We)|(Th)|(Fr)|(Sa)|(Su)/g;
    var _weekdays = {"Mo":"Monday", "Tu":"Tuesday", "We":"Wednesday", "Th":"Thursday", "Fr":"Friday", "Sa":"Saturday", "Su":"Sunday"};
    var timetable = [];
    // Breaks all the courses up. Looks at all the detail after a course name
    var courses = text.split(_courseTitleLookAhead);

    for (var i = 0; i < courses.length; i++) {
        var courseText = courses[i];
        var course = _courseTitleRegex.exec(courseText);
        // Breaks all classes up, looks at all the details after the Course Code or time in a course
        var classes = courseText.split(_timeDayPairOrCourseCodeLookAhead);

        if (!course) continue;

        var currentSection = _sectionTypeRegex.exec(courseText);

        for (var j = 0; j < classes.length; j++) {
            var locationProfPair = _locationProfPair.exec(classes[j]);
            var time = _timeDayPairRegex.exec(classes[j]);
            var semester = _semesterPairRegex.exec(classes[j]);
            var section = _sectionTypeRegex.exec(classes[j]);
            // Check is the current classes segment is a section if it is update the current Section
            currentSection = section ? section : currentSection;

            if (!locationProfPair || !time || !semester) continue;

            semester = fixDateFormat(semester[1], semester[2]);
            var where = locationProfPair[1];
            var professor = locationProfPair[2];
            // Changes McMasters MoTuWe format to full weekdays
            var days = time[1].replace(_dayRegex, function fullWeekday(x){return _weekdays[x] + ",";});
            var daysArray = days.substring(0, days.length-1).split(",");

            for (var k = 0; k < daysArray.length ; k++) {
                var startTimeSplit = convertTo24Hour(time[2]).split(":");
                var startTimeObj = {'hour': parseInt(startTimeSplit[0], 10), 'minute': parseInt(startTimeSplit[1], 10)};
                var endTimeSplit = convertTo24Hour(time[3]).split(":");
                var endTimeObj = {'hour': parseInt(endTimeSplit[0], 10), 'minute': parseInt(endTimeSplit[1], 10)};
                var sem_start = new Date(semester[0]);
                var sem_end = new Date(semester[1]);
                var semesterStartObj = { year: sem_start.getFullYear(), month: sem_start.getMonth(), day: sem_start.getDate()};
                var semesterEndObj = { year: sem_end.getFullYear(), month: sem_end.getMonth(), day: sem_end.getDate()};
                var courseSplit = course[1].split(" ");
                timetable.push({
                    'course_code_faculty' : courseSplit[0],
                    'course_code_number' : courseSplit[1],
                    'course_number' : currentSection[1],
                    'course_name' : course[2],
                    'semester_start' : semesterStartObj,
                    'semester_end' : semesterEndObj,
                    'where' : where,
                    'professor': professor,
                    'day' : daysArray[k],
                    'start_time' : startTimeObj,
                    'end_time' : endTimeObj,
                    'class_section' : currentSection[2],
                    'class_type' : currentSection[3]
                });
            }
        }
    }

    return timetable;
}

fs.readFile(filename, 'utf8', function(err, data) {
    if (err) throw err;
    console.log(ics.buildICS(parse(data)));
});

module.exports.parse = parse;<commit_msg>Made regex public and removed temp object<commit_after>var ics = require('./ics-builder');
var fs = require('fs')
    , filename = process.argv[2];
var _wsRegex = /\s+/;
var _ampm = /:\d{2}[aAPp][mM]/;
var _courseTitleLookAhead = /(?=\b(?:\w{2,}\ \w{1,5})\ -\ (?:[^\r\n]+)\b)/g;
var _courseCodeLookAhead = /(?=\b\d{5}\b)/;
var _courseTitleRegex = /(?:\b(\w{2,}\ \w{1,5})\ -\ ([^\r\n]+)\b)/;
var _dayOfWeekRegex = /([MoTuhWeFrSaS]{2,6})/;
// Check for any string, location can be "SEE CLASS NOTES" or "TBA" or maybe something else
var _locationRegex = /(\s[\-\w ,]+)/;
var _profRegex = /((\b[A-Za-z]+\s)(?:[A-Za-z\-.]+\s?)+|(\b[A-Za-z]+\s))/;
var _semesterRegex = /((?:\d{2}\/\d{2}\/\d{4})|(?:\d{4}\/\d{2}\/\d{2})|(?:\d{4}-\d{2}-\d{2}))/;
var _locationProfPair = new RegExp(_locationRegex.source + _wsRegex.source + _profRegex.source);
var _semesterPairRegex = new RegExp(_semesterRegex.source +  _wsRegex.source + "-" +  _wsRegex.source +  _semesterRegex.source);
var _sectionTypeRegex = /(\d+)\s+([A-Z]{1}\d+)\s+(?=Lecture|Tutorial|Laboratory)([A-Za-z]{3,}\b)/;
var _dayRegex = /(Mo)|(Tu)|(We)|(Th)|(Fr)|(Sa)|(Su)/g;
var _weekdays = {"Mo":"Monday", "Tu":"Tuesday", "We":"Wednesday", "Th":"Thursday", "Fr":"Friday", "Sa":"Saturday", "Su":"Sunday"};

/**
 * Takes in any time and converts it from a 12HR format to the 24HR format for 7:00PM is 19:00 etc
 * @param {String} time
 * @return {String} 24HR formatted time
 */
function convertTo24Hour(time) {
    var hours = parseInt(time.substr(0, 2), 10);
    if(time.indexOf('AM') > -1 && hours == 12) {
        time = time.replace('12', '0');
    }else if(time.indexOf('PM')  > -1 && hours < 12) {
        time = time.replace(hours, (hours + 12));
    }

    return time.replace(/(AM|PM)/, '');
}

/**
 * Takes any date and turns it into the correct format, so javascript gets the right date. MM/DD/YYYY is the format the javascript object reads or YYYY/MM/DD
 * @param {String} startDate
 * @param {String} endDate
 * @return {String Array} [startDate, endDate]  with format MM/DD/YYYY or YYYY/MM/DD or YYYY-MM-DD
 */
function fixDateFormat(sem_start, sem_end) {
    var startDateSplit = sem_start.split("/");
    var endDateSplit = sem_end.split("/");

    var startMonth = parseInt(startDateSplit[0], 10);
    var startDay = parseInt(startDateSplit[1], 10);
    var startYear = parseInt(startDateSplit[2], 10);

    var endMonth = parseInt(endDateSplit[0], 10);
    var endDay = parseInt(endDateSplit[1], 10);
    var endYear = parseInt(endDateSplit[2], 10);

    var dayDiff = endDay - startDay;
    // If it contains the '-' or the first slot is YYYY it is already in correct format
    if (sem_start.indexOf('-') > -1 || startMonth.toString().length === 4) {
        return [sem_start, sem_end]
    }
    // Date is DD/MM/YYYY as month can't be > 12, switch the MM and DD or If the difference is 3 months(Fall/Winter), 1 month(Spring/Summer) or -5 months(multi-semester(Sept-Apr)),
    else if (startMonth > 12 || endMonth > 12 || (startYear === endYear && startMonth > endMonth) ||
    ([1,3,-5].indexOf(dayDiff) > -1 && [1, 5, 7, 9].indexOf(startDay) > -1)) {
        return [[startDay, startMonth, startYear].join("/"),[endDay, endMonth, endYear].join("/")];
    }

    return [sem_start, sem_end]
}

/**
 * Takes in the raw data of timetable and get all the important details including, course name, professor, times etc.
 * @param {String} text
 * @return {Object/JSON} timetable
 */
function parse(text){
    //Check is there is an AM/PM, if not use other pattern
    var _timeRegex  =  _ampm.exec(text) ? /([012]?\d\:[0-5]\d[AP]M)/ : /([012]?\d\:[0-5]\d)/;
    var _timeDayPairRegex = new RegExp(_dayOfWeekRegex.source + _wsRegex.source + _timeRegex.source +  _wsRegex.source + "-" +  _wsRegex.source + _timeRegex.source);
    var _timeDayPairLookAhead =  _ampm.exec(text) ? /(?=\b(?:[MoTuhWeFrSaS]{2,6})\s+(?:[012]?\d\:[0-5]\d[AP]M)\s+-\s+(?:[012]?\d\:[0-5]\d[AP]M)\b)/ : /(?=\b(?:[MoTuhWeFrSaS]{2,6})\s+(?:[012]?\d\:[0-5]\d)\s+-\s+(?:[012]?\d\:[0-5]\d)\b)/;
    var _timeDayPairOrCourseCodeLookAhead = new RegExp(_courseCodeLookAhead.source + "|" + _timeDayPairLookAhead.source ,"g");
    var timetable = [];
    // Breaks all the courses up. Looks at all the detail after a course name
    var courses = text.split(_courseTitleLookAhead);

    for (var i = 0; i < courses.length; i++) {
        var courseText = courses[i];
        var course = _courseTitleRegex.exec(courseText);
        // Breaks all classes up, looks at all the details after the Course Code or time in a course
        var classes = courseText.split(_timeDayPairOrCourseCodeLookAhead);

        if (!course) continue;

        var currentSection = _sectionTypeRegex.exec(courseText);

        for (var j = 0; j < classes.length; j++) {
            var locationProfPair = _locationProfPair.exec(classes[j]);
            var time = _timeDayPairRegex.exec(classes[j]);
            var semester = _semesterPairRegex.exec(classes[j]);
            var section = _sectionTypeRegex.exec(classes[j]);
            // Check is the current classes segment is a section if it is update the current Section
            currentSection = section ? section : currentSection;

            if (!locationProfPair || !time || !semester) continue;

            semester = fixDateFormat(semester[1], semester[2]);
            var where = locationProfPair[1];
            var professor = locationProfPair[2];
            // Changes McMasters MoTuWe format to full weekdays
            var days = time[1].replace(_dayRegex, function fullWeekday(x){return _weekdays[x] + ",";});
            var daysArray = days.substring(0, days.length-1).split(",");

            for (var k = 0; k < daysArray.length ; k++) {
                var startTimeSplit = convertTo24Hour(time[2]).split(":");
                var endTimeSplit = convertTo24Hour(time[3]).split(":");
                var sem_start = new Date(semester[0]);
                var sem_end = new Date(semester[1]);
                var courseSplit = course[1].split(" ");
                timetable.push({
                    'course_code_faculty' : courseSplit[0],
                    'course_code_number' : courseSplit[1],
                    'course_number' : currentSection[1],
                    'course_name' : course[2],
                    'semester_start' : { year: sem_start.getFullYear(), month: sem_start.getMonth(), day: sem_start.getDate()},
                    'semester_end' : { year: sem_end.getFullYear(), month: sem_end.getMonth(), day: sem_end.getDate()},
                    'where' : where,
                    'professor': professor,
                    'day' : daysArray[k],
                    'start_time' : {'hour': parseInt(startTimeSplit[0], 10), 'minute': parseInt(startTimeSplit[1], 10)},
                    'end_time' : {'hour': parseInt(endTimeSplit[0], 10), 'minute': parseInt(endTimeSplit[1], 10)},
                    'class_section' : currentSection[2],
                    'class_type' : currentSection[3]
                });
            }
        }
    }

    return timetable;
}

fs.readFile(filename, 'utf8', function(err, data) {
    if (err) throw err;
    console.log(ics.buildICS(parse(data)));
});

module.exports.parse = parse;<|endoftext|>


<commit_before>'use strict';
var ip = require('ip'),
    spawn = require('child_process').spawn,
    request = require('request').defaults({ encoding: null }),
    debug = require('debug')('Camera:Sensr:CameraAccessory');

/**
 * Represents a Sensr.net Camera
 *
 * @param {any} hap
 * @param {any} sensrCameraOptions
 * @param {any} log
 * @see {@link https://github.com/KhaosT/HAP-NodeJS/wiki/IP-Camera|HAP-NodeJS IP Camera Reference}
 * @see {@link https://github.com/KhaosT/homebridge-camera-ffmpeg/|Homebridge Camera FFMPEG}
 * @see {@link https://ffmpeg.org/ffmpeg.html|FFMPEG Reference}
 */
function SensrCamera(hap, sensrCameraOptions, log) {
    var self = this;
    self.HomeBridgeHap = hap;
    self.options = sensrCameraOptions;
    self.log = debug || log || console.log;

    self.HomebridgeHapUuid = hap.uuid;
    self.HomebridgeHapService = hap.Service;
    self.HomebridgeHapCharacteristic = hap.Characteristic;
    self.HomebridgeHapStreamController = hap.StreamController;

    self.log('Configuring a SensrCamera.', self.options);

    self.services = [];
    self.streamControllers = [];

    self.pendingSessions = {};
    self.ongoingSessions = {};

    self.createCameraControlService();
    self._createStreamControllers();
}

SensrCamera.prototype.handleCloseConnection = function (connectionId) {
    var self = this;
    self.log('Closing Connection.', connectionId);
    self.streamControllers.forEach(function (controller) {
        controller.handleCloseConnection(connectionId);
    });
    self.log('Closed Connection.', connectionId);
};

SensrCamera.prototype.handleSnapshotRequest = function (req, callback) {
    var self = this;
    self.log('Generating Snapshot.', self.options.still);

    request({ url: self.options.still }, function (err, response, buffer) {
        callback(err, buffer);
        self.log('Generated Snapshot.', self.options.still);
    });
};

SensrCamera.prototype.prepareStream = function (request, callback) {
    var self = this,
        sessionInfo = {},
        response = {},
        sessionID = request.sessionID,
        targetAddress = request.targetAddress;

    sessionInfo.address = targetAddress;

    self.log('Preparing Stream.', sessionInfo);

    var videoInfo = request.video;

    if (videoInfo) {
        var targetPort = videoInfo.port,
            srtp_key = videoInfo.srtp_key,
            srtp_salt = videoInfo.srtp_salt;

        var videoResp = {
            port: targetPort,
            ssrc: 1,
            srtp_key: srtp_key,
            srtp_salt: srtp_salt
        };

        response.video = videoResp;

        sessionInfo.video_port = targetPort;
        sessionInfo.video_srtp = Buffer.concat([srtp_key, srtp_salt]);
        sessionInfo.video_ssrc = 1;
    }

    var currentAddress = ip.address();
    var addressResp = {
        address: currentAddress
    };

    addressResp.type = ip.isV4Format(currentAddress) ? 'v4' : 'v6';

    response.address = addressResp;
    self.pendingSessions[self.HomebridgeHapUuid.unparse(sessionID)] = sessionInfo;

    callback(response);
    self.log('Prepared Stream.', sessionInfo);
};

SensrCamera.prototype.handleStreamRequest = function (request) {
    var self = this,
        sessionId = request.sessionID,
        requestType = request.type;

    self.log('Handling Stream Request.', sessionId, requestType);

    if (sessionId) {
        var sessionIdentifier = self.HomebridgeHapUuid.unparse(sessionId);

        if (requestType === 'start') {
            var sessionInfo = self.pendingSessions[sessionIdentifier];
            self.log('Starting Stream.', sessionId, sessionIdentifier);

            if (sessionInfo) {
                var width = 1280;
                var height = 720;
                var fps = 30;
                var bitrate = 300;

                var videoInfo = request.video;
                if (videoInfo) {
                    width = videoInfo.width;
                    height = videoInfo.height;

                    var expectedFPS = videoInfo.fps;
                    if (expectedFPS < fps) {
                        fps = expectedFPS;
                    }

                    bitrate = videoInfo.max_bit_rate;
                }

                var targetAddress = sessionInfo.address;
                var targetVideoPort = sessionInfo.video_port;
                var videoKey = sessionInfo.video_srtp;

                var ffmpegCommand = '-i ' + self.options.still + ' -threads 0 -vcodec libx264 -an -pix_fmt yuv420p -r ' + fps + ' -f rawvideo -tune zerolatency -vf scale=' + width + ':' + height + ' -b:v ' + bitrate + 'k -bufsize ' + bitrate + 'k -payload_type 99 -ssrc 1 -f rtp -srtp_out_suite AES_CM_128_HMAC_SHA1_80 -srtp_out_params ' + videoKey.toString('base64') + ' srtp://' + targetAddress + ':' + targetVideoPort + '?rtcpport=' + targetVideoPort + '&localrtcpport=' + targetVideoPort + '&pkt_size=1378';
                self.log(ffmpegCommand);
                var ffmpeg = spawn('ffmpeg', ffmpegCommand.split(' '), { env: process.env });
                self.ongoingSessions[sessionIdentifier] = ffmpeg;
            }

            delete this.pendingSessions[sessionIdentifier];
        } else if (requestType === 'stop') {
            self.log('Stopping Stream.', sessionId, sessionIdentifier);
            var ffmpegProcess = this.ongoingSessions[sessionIdentifier];
            if (ffmpegProcess) {
                ffmpegProcess.kill('SIGKILL');
            }

            delete self.ongoingSessions[sessionIdentifier];
        }
    }

    self.log('Handled Stream Request.', sessionId, requestType);
};

SensrCamera.prototype.createCameraControlService = function () {
    var self = this;
    self.log('Creating Camera Control Service');

    var controlService = new self.HomebridgeHapService.CameraControl();

    self.services.push(controlService);
    self.log('Created Camera Control Service');
};

SensrCamera.prototype._createStreamControllers = function (options) {
    var self = this,
        // TODO: add support to override these options?
        maxStreams = 2,
        maxWidth = 640,
        maxHeight = 480,
        fps = 3;

    self.log('Creating Camera Stream Controller(s)');

    options = options || {
        srtp: true,
        video: {
            codec: {
                // Enum, refer to StreamController.VideoCodecParamProfileIDTypes
                profiles: [0, 1, 2],
                // Enum, refer to StreamController.VideoCodecParamLevelTypes
                levels: [0, 1, 2]
            },
            resolutions: [
                [maxWidth, maxHeight, fps]
            ]
        },
        // We don't really use this, since there is no sound from the JPEG stream
        audio: {
            codecs: [
                {
                    type: "OPUS",
                    samplerate: 16
                }
            ]
        }
    };

    for (var i = 0; i < maxStreams; i++) {
        var streamController = new self.HomebridgeHapStreamController(i, options, self);

        self.services.push(streamController.service);
        self.streamControllers.push(streamController);
    }

    self.log('Created Camera Stream Controller(s)');
};

module.exports = SensrCamera;<commit_msg>adding name to track logs<commit_after>'use strict';
var ip = require('ip'),
    spawn = require('child_process').spawn,
    request = require('request').defaults({ encoding: null }),
    debug = require('debug')('Camera:Sensr:CameraAccessory');

/**
 * Represents a Sensr.net Camera
 *
 * @param {any} hap
 * @param {any} sensrCameraOptions
 * @param {any} log
 * @see {@link https://github.com/KhaosT/HAP-NodeJS/wiki/IP-Camera|HAP-NodeJS IP Camera Reference}
 * @see {@link https://github.com/KhaosT/homebridge-camera-ffmpeg/|Homebridge Camera FFMPEG}
 * @see {@link https://ffmpeg.org/ffmpeg.html|FFMPEG Reference}
 */
function SensrCamera(hap, sensrCameraOptions, log) {
    var self = this;
    self.name = sensrCameraOptions.name;
    self.HomeBridgeHap = hap;
    self.options = sensrCameraOptions;
    self.log = debug || log || console.log;

    self.HomebridgeHapUuid = hap.uuid;
    self.HomebridgeHapService = hap.Service;
    self.HomebridgeHapCharacteristic = hap.Characteristic;
    self.HomebridgeHapStreamController = hap.StreamController;

    self.log(self.name, 'Configuring a SensrCamera.', self.options);

    self.services = [];
    self.streamControllers = [];

    self.pendingSessions = {};
    self.ongoingSessions = {};

    self.createCameraControlService();
    self._createStreamControllers();
}

SensrCamera.prototype.handleCloseConnection = function (connectionId) {
    var self = this;
    self.log(self.name, 'Closing Connection.', connectionId);
    self.streamControllers.forEach(function (controller) {
        controller.handleCloseConnection(connectionId);
    });
    self.log(self.name, 'Closed Connection.', connectionId);
};

SensrCamera.prototype.handleSnapshotRequest = function (req, callback) {
    var self = this;
    self.log(self.name, 'Generating Snapshot.', self.options.still);

    request({ url: self.options.still }, function (err, response, buffer) {
        callback(err, buffer);
        self.log(self.name, 'Generated Snapshot.', self.options.still);
    });
};

SensrCamera.prototype.prepareStream = function (request, callback) {
    var self = this,
        sessionInfo = {},
        response = {},
        sessionID = request.sessionID,
        targetAddress = request.targetAddress;

    sessionInfo.address = targetAddress;

    self.log(self.name, 'Preparing Stream.', sessionInfo);

    var videoInfo = request.video;

    if (videoInfo) {
        var targetPort = videoInfo.port,
            srtp_key = videoInfo.srtp_key,
            srtp_salt = videoInfo.srtp_salt;

        var videoResp = {
            port: targetPort,
            ssrc: 1,
            srtp_key: srtp_key,
            srtp_salt: srtp_salt
        };

        response.video = videoResp;

        sessionInfo.video_port = targetPort;
        sessionInfo.video_srtp = Buffer.concat([srtp_key, srtp_salt]);
        sessionInfo.video_ssrc = 1;
    }

    var currentAddress = ip.address();
    var addressResp = {
        address: currentAddress
    };

    addressResp.type = ip.isV4Format(currentAddress) ? 'v4' : 'v6';

    response.address = addressResp;
    self.pendingSessions[self.HomebridgeHapUuid.unparse(sessionID)] = sessionInfo;

    callback(response);
    self.log(self.name, 'Prepared Stream.', sessionInfo);
};

SensrCamera.prototype.handleStreamRequest = function (request) {
    var self = this,
        sessionId = request.sessionID,
        requestType = request.type;

    self.log(self.name, 'Handling Stream Request.', sessionId, requestType);

    if (sessionId) {
        var sessionIdentifier = self.HomebridgeHapUuid.unparse(sessionId);

        if (requestType === 'start') {
            var sessionInfo = self.pendingSessions[sessionIdentifier];
            self.log(self.name, 'Starting Stream.', sessionId, sessionIdentifier);

            if (sessionInfo) {
                var width = 1280;
                var height = 720;
                var fps = 30;
                var bitrate = 300;

                var videoInfo = request.video;
                if (videoInfo) {
                    width = videoInfo.width;
                    height = videoInfo.height;

                    var expectedFPS = videoInfo.fps;
                    if (expectedFPS < fps) {
                        fps = expectedFPS;
                    }

                    bitrate = videoInfo.max_bit_rate;
                }

                var targetAddress = sessionInfo.address;
                var targetVideoPort = sessionInfo.video_port;
                var videoKey = sessionInfo.video_srtp;

                var ffmpegCommand = '-i ' + self.options.still + ' -threads 0 -vcodec libx264 -an -pix_fmt yuv420p -r ' + fps + ' -f rawvideo -tune zerolatency -vf scale=' + width + ':' + height + ' -b:v ' + bitrate + 'k -bufsize ' + bitrate + 'k -payload_type 99 -ssrc 1 -f rtp -srtp_out_suite AES_CM_128_HMAC_SHA1_80 -srtp_out_params ' + videoKey.toString('base64') + ' srtp://' + targetAddress + ':' + targetVideoPort + '?rtcpport=' + targetVideoPort + '&localrtcpport=' + targetVideoPort + '&pkt_size=1378';
                self.log(self.name, ffmpegCommand);
                var ffmpeg = spawn('ffmpeg', ffmpegCommand.split(' '), { env: process.env });
                self.ongoingSessions[sessionIdentifier] = ffmpeg;
            }

            delete this.pendingSessions[sessionIdentifier];
        } else if (requestType === 'stop') {
            self.log(self.name, 'Stopping Stream.', sessionId, sessionIdentifier);
            var ffmpegProcess = this.ongoingSessions[sessionIdentifier];
            if (ffmpegProcess) {
                ffmpegProcess.kill('SIGKILL');
            }

            delete self.ongoingSessions[sessionIdentifier];
        }
    }

    self.log(self.name, 'Handled Stream Request.', sessionId, requestType);
};

SensrCamera.prototype.createCameraControlService = function () {
    var self = this;
    self.log(self.name, 'Creating Camera Control Service');

    var controlService = new self.HomebridgeHapService.CameraControl();

    self.services.push(controlService);
    self.log(self.name, 'Created Camera Control Service');
};

SensrCamera.prototype._createStreamControllers = function (options) {
    var self = this,
        // TODO: add support to override these options?
        maxStreams = 2,
        maxWidth = 640,
        maxHeight = 480,
        fps = 3;

    self.log(self.name, 'Creating Camera Stream Controller(s)');

    options = options || {
        srtp: true,
        video: {
            codec: {
                // Enum, refer to StreamController.VideoCodecParamProfileIDTypes
                profiles: [0, 1, 2],
                // Enum, refer to StreamController.VideoCodecParamLevelTypes
                levels: [0, 1, 2]
            },
            resolutions: [
                [maxWidth, maxHeight, fps]
            ]
        },
        // We don't really use this, since there is no sound from the JPEG stream
        audio: {
            codecs: [
                {
                    type: "OPUS",
                    samplerate: 16
                }
            ]
        }
    };

    for (var i = 0; i < maxStreams; i++) {
        var streamController = new self.HomebridgeHapStreamController(i, options, self);

        self.services.push(streamController.service);
        self.streamControllers.push(streamController);
    }

    self.log(self.name, 'Created Camera Stream Controller(s)');
};

module.exports = SensrCamera;<|endoftext|>


<commit_before>/**
 * A self connecting Websocket client-client proxy.
 *
 * expects two arguments like: ws://source.socket.com:port/path ws://dest.socket.com:port/path
 * where the source is a kind of websocket slave proxy (slaveproxy.js)
 *
 */

function WSAutoconnectProxy(config){

	var WSocket = require('ws');

	/**
	 * creates a half connected socket. that immediately connects to the source, and once data is recieved, connects to the destination.
	 */

	(new WSocket(config.source)).on('open',function(){

		console.log('connected proxy');

	}).once('message', function message(data, flags) {

		var a=this;

		(new WSocket(config.destination)).on('open', function() {

			var b=this;
			b.send(data);
			a.on('message', function message(data, flags) {
				b.send(data);
			}).on('error',function(error){
				console.log('a error: '+error)
			}).on('close',function(code, message){
				console.log('a close: '+code+' '+message);
				a=null;
				if(b){
					b.close();
				}
			});

			b.on('message', function message(data, flags) {
				a.send(data);
			}).on('error',function(error){
				console.log('b error: '+error)
			}).on('close',function(code, message){
				console.log('b close: '+code+' '+message);
				b=null;
				if(a){
					a.close();
				}

			});

		});

		new WSAutoconnectProxy(config);

	});
};

module.exports=WSAutoconnectProxy;

if(process.argv){

	console.log(process.argv);

	var fs=require('fs');
	fs.realpath(process.argv[1],function(err, p1){
		fs.realpath(__filename,function(err, p2){

			console.log(p1+' '+p2);

			if(p1===p2){

				if(process.argc!=4){
					throw new Error('Requires websocket source and destination address arguments: ie: www.host.com:port/path');
				}

				if(process.argv[2].indexOf('ws:')!==0){
					throw new Error('Requires websocket source address argument: ie: www.host.com:port/path');
				}

				if(process.argv[3].indexOf('ws:')!==0){
					throw new Error('Requires websocket destination address argument: ie: www.host.com:port/path');
				}

				new WSAutoconnectProxy({source:process.argv[2], destination:process.argv[3]});

			}

		});
	});
}
<commit_msg>changed a, b to source dest<commit_after>/**
 * A self connecting Websocket client-client proxy.
 *
 * expects two arguments like: ws://user:pass@source.socket.com:port/path ws://dest.socket.com:port/path
 * where the source is a kind of websocket slave proxy (slaveproxy.js)
 *
 */

function WSAutoconnectProxy(config){

	var me=this;

	me.__primeSourceConnection(config);

};

WSAutoconnectProxy.prototype._primeSourceConnection=function(config){
	var me=this;
	if(!me._sourceConnections){
		me._sourceConnections=[];
		me._destConnections=[];
	}

	var WSocket = require('ws');

	/**
	 * creates a half connected socket. that immediately connects to the source, and once data is recieved, connects to the destination.
	 */

	var source=(new WSocket(config.source)).on('open',function(){
		me._sourceConnections.push(source);
		console.log('connected proxy');

	}).once('message', function message(data, flags) {

		var dest=(new WSocket(config.destination)).on('open', function() {
			me._destConnections.push(dest);

			dest.send(data);

			source.on('message', function message(data, flags) {
				dest.send(data);
			}).on('error',function(error){
				console.log('a error: '+error)
			}).on('close',function(code, message){
				console.log('a close: '+code+' '+message);
				source=null;
				if(dest){
					dest.close();
				}
			});

			dest.on('message', function message(data, flags) {
				source.send(data);
			}).on('error',function(error){
				console.log('b error: '+error)
			}).on('close',function(code, message){
				console.log('b close: '+code+' '+message);

				dest=null;
				if(source){
					source.close();
				}

			});

		});

		me.__primeSourceConnection(config);

	});

}

module.exports=WSAutoconnectProxy;

if(process.argv){

	console.log(process.argv);

	var fs=require('fs');
	fs.realpath(process.argv[1],function(err, p1){
		fs.realpath(__filename,function(err, p2){

			console.log(p1+' '+p2);

			if(p1===p2){

				if(process.argc!=4){
					throw new Error('Requires websocket source and destination address arguments: ie: www.host.com:port/path');
				}

				if(process.argv[2].indexOf('ws:')!==0){
					throw new Error('Requires websocket source address argument: ie: www.host.com:port/path');
				}

				if(process.argv[3].indexOf('ws:')!==0){
					throw new Error('Requires websocket destination address argument: ie: www.host.com:port/path');
				}

				new WSAutoconnectProxy({source:process.argv[2], destination:process.argv[3]});

			}

		});
	});
}
<|endoftext|>


<commit_before>var eventbus;
var eventbus2;

var App = (function () {
    function App() {

    }

    App.prototype.singleComponent = function () {
        eventbus = new Eventbus();

        // TODO publisher's data model for MVVM pattern
        // TODO publisher's computed properties
        var pub = new Publisher('event1', [
            {name: 'Sumer'},
            {name: 'Hitit'},
            {name: 'Hatti'},
            {name: 'Hurri'},
            {name: 'İskit'}
        ]);

        eventbus.publisher().register(pub);

        var component = new Component('myComponent', {
            event: 'event1',
            render: function (state) {
                var ce = Component.createElement;

                var ul = ce("ul", {class: "list-group"});

                for (var i in state) {
                    if (state.hasOwnProperty(i)) {
                        var li = Component.createElement("li", {class: "list-group-item", text: state[i].name});
                        ul.append(li);
                    }
                }

                return ul;
            },
            methods: { // or Component.createElement 's on function
                'li': { // all selectors; (.), (#), (tag name)
                    click: function (e) {
                        console.log(this.targetElement);
                    }
                },
                '#myLi': {
                    mouseenter: function (e) {
                        console.log("mouse enter");
                    }
                }
            }
        });

        //Routing
        var anatolia = new Anatolia({
            name: 'AnatoliaApp',
            eventbus: eventbus,
            components: {
                '#component_1': component
            }
        });

        anatolia.render();

        // anatolia.listen();
    };

    App.prototype.nestedComponents = function () {
        eventbus = new Eventbus(); //TODO publsher change event with object propert set event

        // TODO publisher's data model for MVVM pattern
        // TODO publisher's computed properties
        var pub = new Publisher('event1', [
            {order: 1, name: 'Sumer'},
            {order: 2, name: 'Hitit'},
            {order: 3, name: 'Hatti'},
            {order: 4, name: 'Hurri'},
            {order: 5, name: 'İskit'}
        ]);

        var stylePub = new Publisher('styleEvent',
            {
                class: "table table-condensed table-striped table-hover",
                header: [
                    {text: "Order"},
                    {text: "Name"}
                ]
            }
        );

        eventbus.publisher().register(pub, stylePub);

        var tableComponent = new Component('table', {
            render: function (state) {
                var table = Component.createElement("table", {class: state.class});
                var thead = Component.createElement("thead"); // TODO  Component.createElement("table.table.table-condensed.table-striped > thead - tbody")
                var tbody = Component.createElement("tbody");
                var theadRow = Component.createElement("tr");

                Component.createElement("table.table.table-condensed.table-striped > thead,tbody");

                for(var i in state.header) {
                    if(state.header.hasOwnProperty(i)) {
                        var headerCell = Component.createElement("th", {text: state.header[i].text});
                        theadRow.append(headerCell);
                    }
                }

                thead.append(theadRow);
                table.append(thead);
                table.append(tbody);

                return table;
            }
        });

        var component = new Component('myComponent', {
            event: 'event1',
            render: function (state) {
                var $$ = Component.createElement;

                //TODO re-render containerless components when subscriber fired
                var table = tableComponent.setEventbus(eventbus).setEvent("styleEvent").render();

                for (var i in state) {
                    if (state.hasOwnProperty(i)) {
                        var tr = $$("tr");

                        var orderCell = $$("td", {text: state[i].order});
                        var nameCell = $$("td", {text: state[i].name});

                        tr.append(orderCell, nameCell);
                        table.children[1].append(tr);
                    }
                }

                return table;
            },
            methods: { // or Component.createElement 's on function
                'li': { // all selectors; (.), (#), (tag name)
                    click: function (e) {
                        console.log(this.targetElement);
                    }
                },
                '#myLi': {
                    mouseenter: function (e) {
                        console.log("mouse enter");
                    }
                }
            }
        });

        //Routing
        var anatolia = new Anatolia({
            name: 'AnatoliaApp',
            eventbus: eventbus,
            components: {
                '#component_1': component
            }
        });

        anatolia.render();
    };

    return App;
})();<commit_msg>reusable component<commit_after>var eventbus;
var eventbus2;

var App = (function () {
    function App() {

    }

    App.prototype.singleComponent = function () {
        eventbus = new Eventbus();

        // TODO publisher's data model for MVVM pattern
        // TODO publisher's computed properties
        var pub = new Publisher('event1', [
            {name: 'Sumer'},
            {name: 'Hitit'},
            {name: 'Hatti'},
            {name: 'Hurri'},
            {name: 'İskit'}
        ]);

        eventbus.publisher().register(pub);

        var component = new Component('myComponent', {
            event: 'event1',
            render: function (state) {
                var ce = Component.createElement;

                var ul = ce("ul", {class: "list-group"});

                for (var i in state) {
                    if (state.hasOwnProperty(i)) {
                        var li = Component.createElement("li", {class: "list-group-item", text: state[i].name});
                        ul.append(li);
                    }
                }

                return ul;
            },
            methods: { // or Component.createElement 's on function
                'li': { // all selectors; (.), (#), (tag name)
                    click: function (e) {
                        console.log(this.targetElement);
                    }
                },
                '#myLi': {
                    mouseenter: function (e) {
                        console.log("mouse enter");
                    }
                }
            }
        });

        //Routing
        var anatolia = new Anatolia({
            name: 'AnatoliaApp',
            eventbus: eventbus,
            components: {
                '#component_1': component
            }
        });

        anatolia.render();

        // anatolia.listen();
    };

    App.prototype.nestedComponents = function () {
        eventbus = new Eventbus(); //TODO publsher change event with object propert set event

        // TODO publisher's data model for MVVM pattern
        // TODO publisher's computed properties
        var pub = new Publisher('event1', [
            {order: 1, name: 'Sumer'},
            {order: 2, name: 'Hitit'},
            {order: 3, name: 'Hatti'},
            {order: 4, name: 'Hurri'},
            {order: 5, name: 'İskit'}
        ]);

        var stylePub = new Publisher('styleEvent',
            {
                class: "table table-condensed table-striped table-hover",
                header: [
                    {text: "Order"},
                    {text: "Name"}
                ]
            }
        );

        eventbus.publisher().register(pub, stylePub);

        var tableComponent = new Component('table', {
            render: function (state) {
                var table = Component.createElement("table", {class: state.class});
                var thead = Component.createElement("thead"); // TODO  Component.createElement("table.table.table-condensed.table-striped > thead - tbody")
                var tbody = Component.createElement("tbody");
                var theadRow = Component.createElement("tr");

                for(var i in state.header) {
                    if(state.header.hasOwnProperty(i)) {
                        var headerCell = Component.createElement("th", {text: state.header[i].text});
                        theadRow.append(headerCell);
                    }
                }

                thead.append(theadRow);
                table.append(thead);
                table.append(tbody);

                return table;
            }
        });

        var component = new Component('myComponent', {
            event: 'event1',
            render: function (state) {
                var $$ = Component.createElement;

                //TODO re-render containerless components when subscriber fired
                var table = tableComponent.setEventbus(eventbus).setEvent("styleEvent").render();

                for (var i in state) {
                    if (state.hasOwnProperty(i)) {
                        var tr = $$("tr");

                        var orderCell = $$("td", {text: state[i].order});
                        var nameCell = $$("td", {text: state[i].name});

                        tr.append(orderCell, nameCell);
                        table.children[1].append(tr);
                    }
                }

                return table;
            },
            methods: { // or Component.createElement 's on function
                'li': { // all selectors; (.), (#), (tag name)
                    click: function (e) {
                        console.log(this.targetElement);
                    }
                },
                '#myLi': {
                    mouseenter: function (e) {
                        console.log("mouse enter");
                    }
                }
            }
        });

        //Routing
        var anatolia = new Anatolia({
            name: 'AnatoliaApp',
            eventbus: eventbus,
            components: {
                '#component_1': component
            }
        });

        anatolia.render();
    };

    return App;
})();<|endoftext|>


<commit_before>
module.exports = () => {
	let div = document.createElement('div');
	div.innerHTML = `<video></video>`;
	div.style.background = '#000';
	div.style.position = 'fixed';
	div.style.top = '0px';
	div.style.left = '0px';
	div.style.zIndex = '999999';

	let video = div.querySelector('video');
	video.autoplay = true;
	video.controls = true;
	video.style.position = 'absolute'
	video.style.display = 'none'

	let self = {video, div};

	let resize = () => {
		let windowRatio = window.innerHeight/window.innerWidth;
		let videoRatio = video.videoHeight/video.videoWidth;

		if (videoRatio > windowRatio) {
			let width = window.innerHeight/videoRatio;
			video.style.height = window.innerHeight+'px';
			video.style.width = width+'px';
			video.style.left = (window.innerWidth-width)/2+'px';
			video.style.top = '0px';
		} else {
			let height = window.innerWidth*videoRatio;
			video.style.width = window.innerWidth+'px';
			video.style.height = height+'px';
			video.style.top = (window.innerHeight-height)/2+'px';
			video.style.left = '0px';
		}

		div.style.height = window.innerHeight+'px';
		div.style.width = window.innerWidth+'px';
	}

	let onStarted = () => {
		video.style.display = 'block';
		video.removeEventListener('canplay', onStarted);
		resize();
		if (self.onStarted)
			self.onStarted();
	}
	video.addEventListener('canplay', onStarted);

	let toggleFullScreen = () => {
		if (!document.webkitFullscreenElement) {
			div.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
		} else {
			document.webkitCancelFullScreen();
		}
	}

	let togglePlayPause = () => {
		if (video.paused)
			video.play();
		else
			video.pause();
	}

	function doubleclick(el, onsingle, ondouble) {
		let dbl;
		return () => {
			if (!dbl) {
				dbl = true;
				setTimeout(function () {
					if (dbl) {
						onsingle();
					}
					dbl = false;
				}, 300);
			} else {
				dbl = false;
				ondouble();
			}
		}
	}

	video.addEventListener('mousedown', doubleclick(video, () => {
		togglePlayPause();
	}, () => {
		toggleFullScreen();
	}))

	let seekDelta = 5.0;
	let doSeek = delta => {
		let cur = self.streams.findNearestIndexByTime(video.currentTime);
		let to;
		let base = self.streams.keyframes[cur].time;
		let inc = delta<0?-1:1;
		for (let i = cur; i>=0&&i<self.streams.keyframes.length; i += inc) {
			let time = self.streams.keyframes[i].time;
			to = i;
			if (Math.abs(time-base) > Math.abs(delta)) {
				break;
			}
		}
		video.currentTime = self.streams.keyframes[to].time;
	}
	let seekBack = () => doSeek(-seekDelta)
	let seekForward = () => doSeek(+seekDelta)

	let volumeDelta = 0.2;
	let volumeUp = () => {
		video.volume += volumeDelta;
	}
	let volumeDown = () => {
		video.volume -= volumeDelta;
	}

	let toggleMute = () => {
		video.muted = !video.muted;
	}

	document.body.addEventListener('keydown', (e) => {
		switch (e.code) {
			case "Space": {
				togglePlayPause();
			} break;

			case "ArrowUp": {
				volumeUp();
			} break;

			case "KeyM": {
				toggleMute();
			} break;

			case "ArrowDown": {
				volumeDown();
			} break;

			case "ArrowLeft": {
				seekBack();
			} break;

			case "ArrowRight": {
				seekForward();
			} break;

			case "Enter": {
				if (e.metaKey || e.ctrlKey)
					toggleFullScreen();
			} break;
		}
	});

	document.body.style.margin = 0;
	document.body.appendChild(div);
	resize();
	window.addEventListener('resize', resize);

	return self;
}

<commit_msg>improve player<commit_after>
module.exports = () => {
	let div = document.createElement('div');
	div.innerHTML = `<video></video>`;
	div.style.background = '#000';
	div.style.position = 'fixed';
	div.style.top = '0px';
	div.style.left = '0px';
	div.style.zIndex = '999999';

	let video = div.querySelector('video');
	video.autoplay = true;
	video.controls = true;
	video.style.position = 'absolute'
	video.style.display = 'none'

	let self = {video, div};

	let resize = () => {
		let windowRatio = window.innerHeight/window.innerWidth;
		let videoRatio = video.videoHeight/video.videoWidth;

		if (videoRatio > windowRatio) {
			let width = window.innerHeight/videoRatio;
			video.style.height = window.innerHeight+'px';
			video.style.width = width+'px';
			video.style.left = (window.innerWidth-width)/2+'px';
			video.style.top = '0px';
		} else {
			let height = window.innerWidth*videoRatio;
			video.style.width = window.innerWidth+'px';
			video.style.height = height+'px';
			video.style.top = (window.innerHeight-height)/2+'px';
			video.style.left = '0px';
		}

		div.style.height = window.innerHeight+'px';
		div.style.width = window.innerWidth+'px';
	}

	let onStarted = () => {
		video.style.display = 'block';
		video.removeEventListener('loadedmetadata', onStarted);
		resize();
		if (self.onStarted)
			self.onStarted();
	}
	video.addEventListener('loadedmetadata', onStarted);

	let toggleFullScreen = () => {
		if (!document.webkitFullscreenElement) {
			div.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
		} else {
			document.webkitCancelFullScreen();
		}
	}

	let togglePlayPause = () => {
		if (video.paused)
			video.play();
		else
			video.pause();
	}

	function doubleclick(el, onsingle, ondouble) {
		let dbl;
		return () => {
			if (!dbl) {
				dbl = true;
				setTimeout(function () {
					if (dbl) {
						onsingle();
					}
					dbl = false;
				}, 300);
			} else {
				dbl = false;
				ondouble();
			}
		}
	}

	video.addEventListener('mousedown', doubleclick(video, () => {
		togglePlayPause();
	}, () => {
		toggleFullScreen();
	}))

	let seekDelta = 5.0;
	let doSeek = delta => {
		let cur = self.streams.findNearestIndexByTime(video.currentTime);
		let to;
		let base = self.streams.keyframes[cur].time;
		let inc = delta<0?-1:1;
		for (let i = cur; i>=0&&i<self.streams.keyframes.length; i += inc) {
			let time = self.streams.keyframes[i].time;
			to = i;
			if (Math.abs(time-base) > Math.abs(delta)) {
				break;
			}
		}
		video.currentTime = self.streams.keyframes[to].time;
	}
	let seekBack = () => doSeek(-seekDelta)
	let seekForward = () => doSeek(+seekDelta)

	let volumeDelta = 0.2;
	let volumeUp = () => {
		video.volume += volumeDelta;
	}
	let volumeDown = () => {
		video.volume -= volumeDelta;
	}

	let toggleMute = () => {
		video.muted = !video.muted;
	}

	document.body.addEventListener('keydown', (e) => {
		switch (e.code) {
			case "Space": {
				togglePlayPause();
			} break;

			case "ArrowUp": {
				volumeUp();
			} break;

			case "KeyM": {
				toggleMute();
			} break;

			case "ArrowDown": {
				volumeDown();
			} break;

			case "ArrowLeft": {
				seekBack();
			} break;

			case "ArrowRight": {
				seekForward();
			} break;

			case "Enter": {
				if (e.metaKey || e.ctrlKey)
					toggleFullScreen();
			} break;
		}
	});

	document.body.style.margin = 0;
	document.body.appendChild(div);
	resize();
	window.addEventListener('resize', resize);

	return self;
}

<|endoftext|>


<commit_before>var socket = io();

// Handle when the user submits a name to the tree
$('form').submit(function(e){
  e.preventDefault();

  // Send the name to the server to be verified as appropriate
  socket.emit('verify name', $('#name').val());

  // Remove error banner from form
  $('.errorNotification').remove();
});

// Handle when the user enters an inappropriate name
socket.on('blacklisted name', function(msg){
  // Display error notification
  errorNotify();

  // Remove name from textbox
  $('#name').empty();
});

// Handle the end of the interaction between the user and the tree
socket.on('thank you', function(){
  // Remove form
  $('form').remove();

  // Display a form that thanks the user
  $('.registration').append($('

'), $('
').text('Thanks!'));
});

// Create an error banner
function errorNotify()
{
  // Create the banner
  var error = $('
')
    .addClass('errorNotification')
    .text('Error: Name was not accepted')
    .append(
      // Create the exit button for the banner
      $('
')
        .addClass('exitButton')
        .text('X')
        .click(function() {
          error.fadeOut(250);
        })
      )
    .prependTo($(document.body));
}
<commit_msg>Form: Remove bad name<commit_after>var socket = io();

// Handle when the user submits a name to the tree
$('form').submit(function(e){
  e.preventDefault();

  // Send the name to the server to be verified as appropriate
  socket.emit('verify name', $('#name').val());

  // Remove error banner from form
  $('.errorNotification').remove();
});

// Handle when the user enters an inappropriate name
socket.on('blacklisted name', function(msg){
  // Display error notification
  errorNotify();

  // Remove name from textbox
  $('#name').val('');
});

// Handle the end of the interaction between the user and the tree
socket.on('thank you', function(){
  // Remove form
  $('form').remove();

  // Display a form that thanks the user
  $('.registration').append($('

'), $('
').text('Thanks!'));
});

// Create an error banner
function errorNotify()
{
  // Create the banner
  var error = $('
')
    .addClass('errorNotification')
    .text('Error: Name was not accepted')
    .append(
      // Create the exit button for the banner
      $('
')
        .addClass('exitButton')
        .text('X')
        .click(function() {
          error.fadeOut(250);
        })
      )
    .prependTo($(document.body));
}
<|endoftext|>