<commit_before>/**
 * Copyright 2017 aixigo AG
 * Released under the MIT license.
 * https://opensource.org/licenses/MIT
 */

'use strict';

const http = require( 'http' );
const path = require( 'path' );
const java = require( 'java' );
const portfinder = require( 'portfinder' );
const loaderUtils = require( 'loader-utils' );

const CONFIGURATION = 'com.realobjects.pdfreactor.Configuration';
const PDFREACTOR = 'com.realobjects.pdfreactor.PDFreactor';

module.exports = function (source) {
   const options = loaderUtils.getOptions( this ) || {};
   const classpath = Array.isArray( options.classpath ) ? options.classpath : [ options.classpath ];

   this.cacheable();
   this.async();

   if( !java.isJvmCreated() ) {
      java.options.push('-Djava.awt.headless=true');
      java.registerClient( () => {
         classpath.forEach( cp => {
            if( cp ) {
               java.classpath.push( path.resolve( this.context, cp ) );
            }
         } );
      } );
   }

   java.ensureJvm( () => {
      const pdfconfig = java.newInstanceSync( CONFIGURATION );
      const pdfrenderer = java.newInstanceSync( PDFREACTOR );
      const server = http.createServer( ( req, res ) => {
         const resource = path.join( this.context, req.url.substr( 1 ) );
         this.addDependency( resource );
         this.fs.readFile( resource, ( err, content ) => {
            if( err ) {
               res.statusCode = 404;
               res.statusMessage = 'Not found';
               res.end( err.message );
               return;
            }

            res.statusCode = 200;
            res.statusMessage = 'Ok';
            res.end( content );
         } );
      } );

      pdfconfig.setEnableDebugModeSync( true );
      pdfconfig.setAddLinks( true );
      pdfconfig.setAddBookmarks( true );
      pdfconfig.setDocument( source );

      pipe( [
         ( callback ) => {
            listen( server, callback );
         },
         ( port, callback ) => {
            pdfconfig.setBaseURL( 'http://127.0.0.1:' + port );
            pdfrenderer.convertAsBinary( pdfconfig, callback );
         },
         ( bytes, callback ) => {
            callback( null, Buffer.from( bytes ) );
         }
      ], ( err, buffer ) => {
         this.callback( err, buffer );
         server.close();
      } );
   } );
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

function listen( server, callback ) {
   let tries = 3;

   repeat();

   function repeat() {
      portfinder.getPort( port => {
         server.listen( port, err => {
            if (err && tries-- > 0) {
               setTimeout( repeat, 100 + (Math.random() * 100) );
            }
            else {
               callback(err, port);
            }
         } );
      } );
   }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

function pipe( fns, callback ) {
   const stack = fns.slice();

   iterate( null );

   function iterate( err ) {
      const fn = stack.shift();

      if( err || !fn ) {
         callback.apply( null, arguments );
      }
      else {
         fn.apply( null, Array.prototype.slice.call( arguments, 1, fn.length ).concat( [ iterate ] ) );
      }
   }
};

<commit_msg>Fix missing port parameter output<commit_after>/**
 * Copyright 2017 aixigo AG
 * Released under the MIT license.
 * https://opensource.org/licenses/MIT
 */

'use strict';

const http = require( 'http' );
const path = require( 'path' );
const java = require( 'java' );
const portfinder = require( 'portfinder' );
const loaderUtils = require( 'loader-utils' );

const CONFIGURATION = 'com.realobjects.pdfreactor.Configuration';
const PDFREACTOR = 'com.realobjects.pdfreactor.PDFreactor';

module.exports = function (source) {
   const options = loaderUtils.getOptions( this ) || {};
   const classpath = Array.isArray( options.classpath ) ? options.classpath : [ options.classpath ];

   this.cacheable();
   this.async();

   if( !java.isJvmCreated() ) {
      java.options.push('-Djava.awt.headless=true');
      java.registerClient( () => {
         classpath.forEach( cp => {
            if( cp ) {
               java.classpath.push( path.resolve( this.context, cp ) );
            }
         } );
      } );
   }

   java.ensureJvm( () => {
      const pdfconfig = java.newInstanceSync( CONFIGURATION );
      const pdfrenderer = java.newInstanceSync( PDFREACTOR );
      const server = http.createServer( ( req, res ) => {
         const resource = path.join( this.context, req.url.substr( 1 ) );
         this.addDependency( resource );
         this.fs.readFile( resource, ( err, content ) => {
            if( err ) {
               res.statusCode = 404;
               res.statusMessage = 'Not found';
               res.end( err.message );
               return;
            }

            res.statusCode = 200;
            res.statusMessage = 'Ok';
            res.end( content );
         } );
      } );

      pdfconfig.setEnableDebugModeSync( true );
      pdfconfig.setAddLinks( true );
      pdfconfig.setAddBookmarks( true );
      pdfconfig.setDocument( source );

      pipe( [
         ( callback ) => {
            listen( server, callback );
         },
         ( port, callback ) => {
            pdfconfig.setBaseURL( 'http://127.0.0.1:' + port );
            pdfrenderer.convertAsBinary( pdfconfig, callback );
         },
         ( bytes, callback ) => {
            callback( null, Buffer.from( bytes ) );
         }
      ], ( err, buffer ) => {
         this.callback( err, buffer );
         server.close();
      } );
   } );
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

function listen( server, callback ) {
   let tries = 3;

   repeat();

   function repeat() {
      portfinder.getPort( (err, port) => {
         if (err) {
            callback(err);
         }
         else {
            server.listen( port, err => {
               if (err && tries-- > 0) {
                  setTimeout( repeat, 100 + (Math.random() * 100) );
               }
               else {
                  callback(err, port);
               }
            } );
         }
      } );
   }
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

function pipe( fns, callback ) {
   const stack = fns.slice();

   iterate( null );

   function iterate( err ) {
      const fn = stack.shift();

      if( err || !fn ) {
         callback.apply( null, arguments );
      }
      else {
         fn.apply( null, Array.prototype.slice.call( arguments, 1, fn.length ).concat( [ iterate ] ) );
      }
   }
};

<|endoftext|>


<commit_before>import React from 'react';

export default React.createClass({
  getInitialState: function () {
    var badge = this.props.badge;
    return {
      imgUrl: 'url(assets/graphics/badges/' +
               badge.category + '-' + (badge.badgeLevel + 1) + '-graphic.svg)',
      progressBreaks: this.getProgressGradientBreaks(badge.points.percentage),
      badgeClass: this.props.badgeClass,
      badgeDescription: badge.description,
      hidden: true
    };
  },
  mouseOver: function () {
    this.setState({hidden: false});
  },

  mouseOut: function () {
    this.setState({hidden: true});
  },
  getProgressGradientBreaks: function (percentage) {
    // Calculates the linear-gradient breakpoint angles necessary to
    // represent progress percentage
    var breakA = 90;
    var breakB = 90;
    if (percentage < 50) {
      breakB = 90 + percentage * 3.6;
      return {A: breakA, B: breakB};
    }
    breakA = -90 + (percentage - 50) * 3.6;
    breakB = 270;
    return {A: breakA, B: breakB};
  },
  render: function () {
    var progressStyle = 'linear-gradient(' + this.state.progressBreaks.A +
                        'deg, #dbdbda 50%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0)),' +
                        'linear-gradient(' + this.state.progressBreaks.B +
                        'deg, #fef387 50%, #dbdbda 50%, #dbdbda)';
    var popup = '';
    if (this.state.badgeClass === 'progress') {
      popup = (

          {this.state.badgeDescription}

      );
    }
    return (

        {popup}

    );
  }
});
<commit_msg>Fixes progress indicator at >50% completion<commit_after>import React from 'react';

export default React.createClass({
  getInitialState: function () {
    var badge = this.props.badge;
    return {
      imgUrl: 'url(assets/graphics/badges/' +
               badge.category + '-' + (badge.badgeLevel + 1) + '-graphic.svg)',
      progressBreaks: this.getProgressGradientBreaks(badge.points.percentage),
      badgeClass: this.props.badgeClass,
      badgeDescription: badge.description,
      hidden: true
    };
  },
  mouseOver: function () {
    this.setState({hidden: false});
  },
  mouseOut: function () {
    this.setState({hidden: true});
  },
  getProgressGradientBreaks: function (percentage) {
    // Calculates the linear-gradient breakpoint angles necessary to
    // represent progress percentage
    var breakA = 90;
    var breakB = 90;
    if (percentage < 50) {
      breakB = 90 + percentage * 3.6;
      return {A: breakA, B: breakB, dependantGrad: '#dbdbda'};
    }
    breakA = -90 + (percentage - 50) * 3.6;
    breakB = 270;
    return {A: breakA, B: breakB, dependantGrad: '#fef387'};
  },
  render: function () {
    var progressStyle = 'linear-gradient(' + this.state.progressBreaks.A +
                        'deg, ' + this.state.progressBreaks.dependantGrad +
                        ' 50%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0)),' +
                        'linear-gradient(' + this.state.progressBreaks.B +
                        'deg, #fef387 50%, #dbdbda 50%, #dbdbda)';
    var popup = '';
    if (this.state.badgeClass === 'progress') {
      popup = (

          {this.state.badgeDescription}

      );
    }
    return (

        {popup}

    );
  }
});
<|endoftext|>


<commit_before>"use strict";

var app = angular.module('speedReadingApp', ['ui-rangeSlider']);

app.controller('MainCtrl', function($scope, $timeout, $window, $http) {

	$scope.settings = {
		'wpm' : 350,
		'wpm_ms' : function() {
			return 60000 / $scope.settings.wpm;
		},
		'pause_between_paragraphs' : true,
		'pause_between_sentences' : true,
		'night_mode' : true,
		'text' : '',
		'highlight_focus_point' : true,
		'sleep' : false,
		'toastDefault' : 'Paste text or URL below',
		'toast' : '',
	};

	$scope.settings.toast = $scope.settings.toastDefault;

	$scope.game = {
		'words' : [],
		'currentWord' : 0,
		'paused' : false,
		'has_started' : false,
		'percentComplete' : function(round) {
			var perc = ($scope.game.currentWord / $scope.game.words.length) * 100,
				ret = round ? perc.toFixed(1) : perc;

			return ret;
		}
	};

	// Clean below and move to directive
	var sleepTimer;
	angular.element('body').bind('mousemove', function() {
		$scope.settings.sleep = false;
		$scope.$apply();
		$timeout.cancel(sleepTimer);
		sleepTimer = $timeout(function() {
			$scope.settings.sleep = true;
			$scope.$apply();
		}, 1000);
	});

	// We merged these two settings, but let's keep them under the hood for now
	// This makes sure the values stay the same
	$scope.$watch('settings.pause_between_sentences', function() {
		$scope.settings.pause_between_paragraphs = $scope.settings.pause_between_sentences;
	});

	// Tried to put this in ng-mousedown, but no luck
	angular.element('#timeline').on('mousedown', function() {
		$scope.pauseRead();
	});

	angular.element(document).on('keyup', function(e) {
		if(e.which === 32) {
			$scope.togglePause();
		}
	});

	/**
	 * Loads saved data from localStorage if found
	 */
	$scope.loadFromSessionStorage = function() {
		if(!localStorage.spread) return false;

		var stored = JSON.parse(localStorage.spread);

		for(var model in stored) {
			var modelValue = stored[model];

			// For checkboxes
			if(modelValue === 'on') {
				modelValue = true;
			}
			if(modelValue === 'off') {
				modelValue = false;
			}

			// Make numbers actual numbers
			if(modelValue == parseInt(modelValue)) {
				modelValue = parseInt(modelValue);
			}

			// Split model name by dot, loop through each level and update
			// the corresponding value in $scope
			model.split('.').reduce(function(result, key, index, array) {
				if(index === array.length-1) {
					result[key] = modelValue;
				}
				return result[key];
			}, $scope);
		}

	}

	$scope.loadFromSessionStorage();

	/**
	 * Prepares for reading then fires off wordLoop
	 */
	$scope.startRead = function() {

		// Bail if already started
		if($scope.game.has_started) {
			return false;
		}

		if($scope.isValidURL($scope.settings.text)) {
			$scope.settings.toast = 'Extracting text from URL...';
			$scope.extractFromUrl($scope.settings.text, function(res) {
				if(res.status === 'success') {
					var text = res.result.betterTrim();
					$scope.settings.text = $scope.makeTextReadable(text);
					$scope.game.words = $scope.splitToWords(text);
					$scope.game.has_started = true;
					$scope.game.paused = false;
					$scope.startCountdown();
					$scope.resetToast();
				} else {
					$scope.settings.toast = 'Error: Could not parse URL';
					$timeout(function() {
						$scope.resetToast();
					}, 3*1000);
				}
			});
		} else {
			$scope.game.words = $scope.splitToWords($scope.settings.text);
			$scope.game.has_started = true;
			$scope.game.paused = false;
			$scope.startCountdown();
		}
	}

	$scope.startCountdown = function() {
		$timeout.cancel($scope.loopTimeout);
		$scope.loopTimeout = $timeout(function() {
			$scope.wordLoop();
		}, 800);
	}

	$scope.stopRead = function() {

		// Bail if not started
		if(!$scope.game.has_started) {
			return false;
		}

		$scope.game.has_started = false;
		$scope.game.paused = false;
		$scope.game.currentWord = 0;
	}

	$scope.restartRead = function() {
		$scope.pauseRead();
		$scope.game.currentWord = 0;
		$scope.continueRead();
	}

	$scope.pauseRead = function() {

		// Bail if not started or already paused
		if(!$scope.game.has_started || $scope.game.paused) {
			return false;
		}

		$scope.game.paused = true;
	}

	/**
	 * Runs when reading is continued from a paused state
	 */
	$scope.continueRead = function(offset) {

		// Bail if we're not paused or not running
		if(!$scope.game.has_started || !$scope.game.paused) {
			return false;
		}

		if(offset) {
			$scope.goToPosition(offset);
		}

		$scope.game.paused = false;
		$scope.game.has_started = true;
		$scope.startCountdown();
	}

	$scope.goToPosition = function(pos) {
		if(pos == 'last_sentence') {
			pos = $scope.findLastSentence();
		}

		$scope.game.currentWord = pos;
	}

	$scope.togglePause = function() {
		if(!$scope.game.has_started) {
			return false;
		}

		if($scope.game.paused) {
			$scope.continueRead();
		} else {
			$scope.pauseRead();
		}
	}

	$scope.extractFromUrl = function(url, callback) {
		url = url.betterTrim();
		$http.get(window.location.href + 'readability.php?url=' + encodeURIComponent(url)) .success(function(data, status) {
			callback(data);
		}).error(function() {
			callback(false);
		});
	}

	$scope.resetToast = function() {
		$scope.settings.toast = $scope.settings.toastDefault;
	}

	$scope.findLastSentence = function() {
		var possible = false,
			currWord = $scope.game.currentWord,
			currWord = currWord > 0 ? currWord-1 : currWord,
			countDown = currWord;

		for(; countDown >= 2; --countDown) {
			var word = $scope.game.words[countDown].value,
				prevWord = $scope.game.words[countDown-1].value,
				secondPrevWord = $scope.game.words[countDown-2].value;

			if( $scope.isBeginningOfSentence(word) && ($scope.isEndOfSentence(prevWord) || $scope.isEndOfSentence(secondPrevWord)) ) {
				return countDown;
			}
		}
		return 0;
	};

	$scope.isBeginningOfSentence = function(word) {
		// Strip whitespace
		word = word.betterTrim().replace(/\s/, '');

		// Cancel if empty (it's a pause)
		if(word === '') {
			return false;
		}

		// Check if first char is uppercase
		if( word.charAt(0) === word.charAt(0).toUpperCase() ) {
			return true;
		}
		return false;
	}

	$scope.isEndOfSentence = function(word) {
		var lastChar = word.slice(-1);

		// Check last char
		if( lastChar === '!' || lastChar === '?' || lastChar === '.' ) {
			return true;
		}
		return false;
	}

	/**
	 * Splits chunk of text into array of words, with spaces between
	 * paragraphs if specified.
	 */
	$scope.splitToWords = function(text) {

		// Remove double spaces, tabs, and new lines, this could be improved
		var text = text.betterTrim().replace(/(\s){2,}/g, '$1'),
			paras = text.split(/[\n]/),
			words = [];

		// Loop through all paragraphs
		for(var i in paras) {
			var para = paras[i],
				paraWords = para.split(' '),
				spaceAfterSentence = false;

			// Loop through all words
			for(w in paraWords) {
				var w = paraWords[w],
					lastChar = w.slice(-1),
					multiplier;

				// Short
				if (w.length <= 4) {
					multiplier = .8;

				// Normal
				} else if (w.length <= 8) {
					multiplier = 1;

				// Long
				} else if(w.length <= 12) {
					multiplier = 1.3;

				// Super long
				} else {
					multiplier = 1.5;
				}

				var highlighted = $scope.highlightFocusPoint(w);

				// Append word to array
				words.push({
					'multiplier': multiplier,
					'value': w,
					'raw': highlighted
				});

				// Append space after word if it's the last word in a
				// sentence, and the setting is on
				spaceAfterSentence = false;
				if($scope.settings.pause_between_sentences) {
					if(lastChar === '.' || lastChar === '?' || lastChar === '!') {
						words.push({
							'multiplier': 2,
							'value': '',
							'raw' : {
								'specialChar' : '(new line)'
							}
						});
						spaceAfterSentence = true;
					}
				}
			}

			// Add space between each paragraph
			if($scope.settings.pause_between_paragraphs && !spaceAfterSentence) {
				words.push({
					'multiplier': 3,
					'value': '',
					'raw' : {
						'specialChar' : '(end paragraph)'
					}
				});
			}
		}

		// Remove last whitespace
		if($scope.settings.pause_between_paragraphs) {
			words.pop();
		}

		return words;
	}

	$scope.highlightFocusPoint = function(word) {
		var breakpoint = .33,
			length = word.length,
			breakAt = Math.floor(length * breakpoint),

			result = {
				start : word.slice(0, breakAt),
				highlighted : word.slice(breakAt, breakAt+1),
				end : word.slice(breakAt+1)
			};

		return result;
	}

	/**
	 * Loop that changes current word. Interval based on specified WPM.
	 */
	$scope.wordLoop = function() {

		// If reading is paused or not started, don't continue
		if($scope.game.has_started === false || $scope.game.paused === true) {
			return false;
		}

		// Unless this is the last word, set timeout for next word
		if ($scope.game.currentWord < $scope.game.words.length) {
			var timeout = $scope.settings.wpm_ms() * $scope.game.words[$scope.game.currentWord].multiplier;

			$timeout(function() {
				// Todo: Clean dis' up
				if($scope.game.has_started === true && $scope.game.paused === false) {
					$scope.game.currentWord += 1;
					$scope.wordLoop();
				}
			}, timeout);

		// Last word, let's hold a second before we go back to the settings
		} else {
			$timeout(function() {
				$scope.stopRead();
			}, 500);
		}
	}

	$scope.makeTextReadable = function(text) {
		return text.betterTrim().replace(/(\r\n|\n|\r)+/gm, '\r\n\r\n'); //.replace(/[\r\n]+/gm, '\r\n\r\n');
	}

	$scope.isValidURL = function(text) {
		return text.betterTrim().match(/^\bhttps?:\/\/?[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]$/);
	}

});

app.filter('unsafe', function($sce) {
    return function(val) {
        return $sce.trustAsHtml(val);
    };
});

app.directive('toggleDropdown', function($timeout) {
	return {
		link: function(scope, elem, attr) {
			angular.element(elem[0]).on('click', function() {
				var dropdown = angular.element(document.getElementById(attr.toggleDropdown));
				if(dropdown.length > 0) {
					dropdown.toggleClass('open');
				}
			});
		}
	}
});

/**
 * Directive to automatically save form field in localStorage.
 * The data saved in localStorage will be automatically set on load.
 * Todo: Perhaps this should watch models instead of DOM elements?
 */
app.directive('saveOnChange', function() {

	// Check support for localStorage before we attempt to save anything
	if(typeof window.localStorage !== void(0)) {
	    return {
	        require: 'ngModel',
	        link: function(scope, elem) {
	        	var elem = angular.element(elem[0]);

	        	// If the element is not bound to a model, do nothing
	        	if(!elem.attr('ng-model')) {
	        		return false;
	        	}

	            // Watch for change and keyup events
	            elem.on('change keyup', function() {
	            	var elem = angular.element(this),
	            		model = elem.attr('ng-model'),
	            		currStored = localStorage.spread ? JSON.parse(localStorage.spread) : {};

	            	// Checkboxes need some special treatment
	            	if(elem[0].tagName === 'INPUT' && elem[0].type === 'checkbox') {
	            		var val = elem[0].checked ? true : false;
	            	} else {
	            		var val = elem.val();
	            	}

	            	currStored[model] = val;
	            	localStorage.spread = JSON.stringify(currStored);
	            });
	        }
	    };

	// No localStorage support, do nothing
	} else {
		return {};
	}

});

String.prototype.betterTrim = function() {
	return this.replace(/\s+(?=\s)/g, '').trim();
};<commit_msg>The "Pause between sentences" setting can now be toggled after the read has started.<commit_after>"use strict";

var app = angular.module('speedReadingApp', ['ui-rangeSlider']);

app.controller('MainCtrl', function($scope, $timeout, $window, $http) {

	$scope.settings = {
		'wpm' : 350,
		'wpm_ms' : function() {
			return 60000 / $scope.settings.wpm;
		},
		'pause_between_paragraphs' : true,
		'pause_between_sentences' : true,
		'night_mode' : true,
		'text' : '',
		'highlight_focus_point' : true,
		'sleep' : false,
		'toastDefault' : 'Paste text or URL below',
		'toast' : '',
	};

	$scope.settings.toast = $scope.settings.toastDefault;

	$scope.game = {
		'words' : [],
		'currentWord' : 0,
		'paused' : false,
		'has_started' : false,
		'percentComplete' : function(round) {
			var perc = ($scope.game.currentWord / $scope.game.words.length) * 100,
				ret = round ? perc.toFixed(1) : perc;

			return ret;
		}
	};

	// Clean below and move to directive
	var sleepTimer;
	angular.element('body').bind('mousemove', function() {
		$scope.settings.sleep = false;
		$scope.$apply();
		$timeout.cancel(sleepTimer);
		sleepTimer = $timeout(function() {
			$scope.settings.sleep = true;
			$scope.$apply();
		}, 1000);
	});

	// We merged these two settings, but let's keep them under the hood for now
	// This makes sure the values stay the same
	$scope.$watch('settings.pause_between_sentences', function() {
		$scope.settings.pause_between_paragraphs = $scope.settings.pause_between_sentences;
	});

	// Tried to put this in ng-mousedown, but no luck
	angular.element('#timeline').on('mousedown', function() {
		$scope.pauseRead();
	});

	angular.element(document).on('keyup', function(e) {
		if(e.which === 32) {
			$scope.togglePause();
		}
	});

	/**
	 * Loads saved data from localStorage if found
	 */
	$scope.loadFromSessionStorage = function() {
		if(!localStorage.spread) return false;

		var stored = JSON.parse(localStorage.spread);

		for(var model in stored) {
			var modelValue = stored[model];

			// For checkboxes
			if(modelValue === 'on') {
				modelValue = true;
			}
			if(modelValue === 'off') {
				modelValue = false;
			}

			// Make numbers actual numbers
			if(modelValue == parseInt(modelValue)) {
				modelValue = parseInt(modelValue);
			}

			// Split model name by dot, loop through each level and update
			// the corresponding value in $scope
			model.split('.').reduce(function(result, key, index, array) {
				if(index === array.length-1) {
					result[key] = modelValue;
				}
				return result[key];
			}, $scope);
		}

	}

	$scope.loadFromSessionStorage();

	/**
	 * Prepares for reading then fires off wordLoop
	 */
	$scope.startRead = function() {

		// Bail if already started
		if($scope.game.has_started) {
			return false;
		}

		if($scope.isValidURL($scope.settings.text)) {
			$scope.settings.toast = 'Extracting text from URL...';
			$scope.extractFromUrl($scope.settings.text, function(res) {
				if(res.status === 'success') {
					var text = res.result.betterTrim();
					$scope.settings.text = $scope.makeTextReadable(text);
					$scope.game.words = $scope.splitToWords(text);
					$scope.game.has_started = true;
					$scope.game.paused = false;
					$scope.startCountdown();
					$scope.resetToast();
				} else {
					$scope.settings.toast = 'Error: Could not parse URL';
					$timeout(function() {
						$scope.resetToast();
					}, 3*1000);
				}
			});
		} else {
			$scope.game.words = $scope.splitToWords($scope.settings.text);
			$scope.game.has_started = true;
			$scope.game.paused = false;
			$scope.startCountdown();
		}
	}

	$scope.startCountdown = function() {
		$timeout.cancel($scope.loopTimeout);
		$scope.loopTimeout = $timeout(function() {
			$scope.wordLoop();
		}, 800);
	}

	$scope.stopRead = function() {

		// Bail if not started
		if(!$scope.game.has_started) {
			return false;
		}

		$scope.game.has_started = false;
		$scope.game.paused = false;
		$scope.game.currentWord = 0;
	}

	$scope.restartRead = function() {
		$scope.pauseRead();
		$scope.game.currentWord = 0;
		$scope.continueRead();
	}

	$scope.pauseRead = function() {

		// Bail if not started or already paused
		if(!$scope.game.has_started || $scope.game.paused) {
			return false;
		}

		$scope.game.paused = true;
	}

	/**
	 * Runs when reading is continued from a paused state
	 */
	$scope.continueRead = function(offset) {

		// Bail if we're not paused or not running
		if(!$scope.game.has_started || !$scope.game.paused) {
			return false;
		}

		if(offset) {
			$scope.goToPosition(offset);
		}

		$scope.game.paused = false;
		$scope.game.has_started = true;
		$scope.startCountdown();
	}

	$scope.goToPosition = function(pos) {
		if(pos == 'last_sentence') {
			pos = $scope.findLastSentence();
		}

		$scope.game.currentWord = pos;
	}

	$scope.togglePause = function() {
		if(!$scope.game.has_started) {
			return false;
		}

		if($scope.game.paused) {
			$scope.continueRead();
		} else {
			$scope.pauseRead();
		}
	}

	$scope.extractFromUrl = function(url, callback) {
		url = url.betterTrim();
		$http.get(window.location.href + 'readability.php?url=' + encodeURIComponent(url)) .success(function(data, status) {
			callback(data);
		}).error(function() {
			callback(false);
		});
	}

	$scope.resetToast = function() {
		$scope.settings.toast = $scope.settings.toastDefault;
	}

	$scope.findLastSentence = function() {
		var possible = false,
			currWord = $scope.game.currentWord,
			currWord = currWord > 0 ? currWord-1 : currWord,
			countDown = currWord;

		for(; countDown >= 2; --countDown) {
			var word = $scope.game.words[countDown].value,
				prevWord = $scope.game.words[countDown-1].value,
				secondPrevWord = $scope.game.words[countDown-2].value;

			if( $scope.isBeginningOfSentence(word) && ($scope.isEndOfSentence(prevWord) || $scope.isEndOfSentence(secondPrevWord)) ) {
				return countDown;
			}
		}
		return 0;
	};

	$scope.isBeginningOfSentence = function(word) {
		// Strip whitespace
		word = word.betterTrim().replace(/\s/, '');

		// Cancel if empty (it's a pause)
		if(word === '') {
			return false;
		}

		// Check if first char is uppercase
		if( word.charAt(0) === word.charAt(0).toUpperCase() ) {
			return true;
		}
		return false;
	}

	$scope.isEndOfSentence = function(word) {
		var lastChar = word.slice(-1);

		// Check last char
		if( lastChar === '!' || lastChar === '?' || lastChar === '.' ) {
			return true;
		}
		return false;
	}

	/**
	 * Splits chunk of text into array of words, with spaces between
	 * paragraphs if specified.
	 */
	$scope.splitToWords = function(text) {

		// Remove double spaces, tabs, and new lines, this could be improved
		var text = text.betterTrim().replace(/(\s){2,}/g, '$1'),
			paras = text.split(/[\n]/),
			words = [];

		// Loop through all paragraphs
		for(var i in paras) {
			var para = paras[i],
				paraWords = para.split(' '),
				spaceAfterSentence = false;

			// Loop through all words
			for(w in paraWords) {
				var w = paraWords[w],
					lastChar = w.slice(-1),
					multiplier;

				// Short
				if (w.length <= 4) {
					multiplier = .8;

				// Normal
				} else if (w.length <= 8) {
					multiplier = 1;

				// Long
				} else if(w.length <= 12) {
					multiplier = 1.3;

				// Super long
				} else {
					multiplier = 1.5;
				}

				var highlighted = $scope.highlightFocusPoint(w);

				// Append word to array
				words.push({
					'type' : 'word',
					'multiplier': multiplier,
					'value': w,
					'raw': highlighted
				});

				// Append space after word if it's the last word in a
				// sentence, and the setting is on
				spaceAfterSentence = false;
				if(lastChar === '.' || lastChar === '?' || lastChar === '!') {
					words.push({
						'type' : 'pause',
						'multiplier': 2,
						'value': '',
						'raw' : {
							'specialChar' : '(new line)'
						}
					});
					spaceAfterSentence = true;
				}
			}

			// Add space between each paragraph
			if(!spaceAfterSentence) {
				words.push({
					'type' : 'pause',
					'multiplier': 3,
					'value': '',
					'raw' : {
						'specialChar' : '(end paragraph)'
					}
				});
			}
		}

		// Remove last whitespace
		if($scope.settings.pause_between_paragraphs) {
			words.pop();
		}

		return words;
	}

	$scope.highlightFocusPoint = function(word) {
		var breakpoint = .33,
			length = word.length,
			breakAt = Math.floor(length * breakpoint),

			result = {
				start : word.slice(0, breakAt),
				highlighted : word.slice(breakAt, breakAt+1),
				end : word.slice(breakAt+1)
			};

		return result;
	}

	/**
	 * Loop that changes current word. Interval based on specified WPM.
	 */
	$scope.wordLoop = function() {

		// If reading is paused or not started, don't continue
		if($scope.game.has_started === false || $scope.game.paused === true) {
			return false;
		}

		var word = $scope.game.words[$scope.game.currentWord];

		// If pause is disabled and the type is a pause, skip this word
		if(!$scope.settings.pause_between_sentences && word.type == 'pause') {
			$scope.game.currentWord += 1;
			$scope.wordLoop();
			return;
		}

		// Unless this is the last word, set timeout for next word
		if ($scope.game.currentWord < $scope.game.words.length) {
			var timeout = $scope.settings.wpm_ms() * word.multiplier;

			$timeout(function() {
				// Todo: Clean dis' up
				if($scope.game.has_started === true && $scope.game.paused === false) {
					$scope.game.currentWord += 1;
					$scope.wordLoop();
				}
			}, timeout);

		// Last word, let's hold a second before we go back to the settings
		} else {
			$timeout(function() {
				$scope.stopRead();
			}, 500);
		}
	}

	$scope.makeTextReadable = function(text) {
		return text.betterTrim().replace(/(\r\n|\n|\r)+/gm, '\r\n\r\n'); //.replace(/[\r\n]+/gm, '\r\n\r\n');
	}

	$scope.isValidURL = function(text) {
		return text.betterTrim().match(/^\bhttps?:\/\/?[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]$/);
	}

});

app.filter('unsafe', function($sce) {
    return function(val) {
        return $sce.trustAsHtml(val);
    };
});

app.directive('toggleDropdown', function($timeout) {
	return {
		link: function(scope, elem, attr) {
			angular.element(elem[0]).on('click', function() {
				var dropdown = angular.element(document.getElementById(attr.toggleDropdown));
				if(dropdown.length > 0) {
					dropdown.toggleClass('open');
				}
			});
		}
	}
});

/**
 * Directive to automatically save form field in localStorage.
 * The data saved in localStorage will be automatically set on load.
 * Todo: Perhaps this should watch models instead of DOM elements?
 */
app.directive('saveOnChange', function() {

	// Check support for localStorage before we attempt to save anything
	if(typeof window.localStorage !== void(0)) {
	    return {
	        require: 'ngModel',
	        link: function(scope, elem) {
	        	var elem = angular.element(elem[0]);

	        	// If the element is not bound to a model, do nothing
	        	if(!elem.attr('ng-model')) {
	        		return false;
	        	}

	            // Watch for change and keyup events
	            elem.on('change keyup', function() {
	            	var elem = angular.element(this),
	            		model = elem.attr('ng-model'),
	            		currStored = localStorage.spread ? JSON.parse(localStorage.spread) : {};

	            	// Checkboxes need some special treatment
	            	if(elem[0].tagName === 'INPUT' && elem[0].type === 'checkbox') {
	            		var val = elem[0].checked ? true : false;
	            	} else {
	            		var val = elem.val();
	            	}

	            	currStored[model] = val;
	            	localStorage.spread = JSON.stringify(currStored);
	            });
	        }
	    };

	// No localStorage support, do nothing
	} else {
		return {};
	}

});

String.prototype.betterTrim = function() {
	return this.replace(/\s+(?=\s)/g, '').trim();
};<|endoftext|>


<commit_before>import assign from 'lodash/assign';
import pick from 'lodash/pick';
import omit from 'lodash/omit';
import React from 'react';
import classes from 'classnames';
import equip from './equip';
import SiteAttribution from './site-attribution';

const smooth = 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms';
const fast = '';

const NewsCard = React.createClass({
    getInitialState() {
        return { right: 0, transition: smooth, opacity: 1 };
    },
    start(e) {
        if (!this.isTouching && !this.isMousing && e.type === 'touchstart') {
            this.origin = { x: e.touches[0].clientX, y: e.touches[0].clientY };
            this.setState({ transition: fast, opacity: 0.8 });
            this.isTouching = true;
        }
        if (!this.isTouching && !this.isMousing && e.type === 'mousedown') {
            this.origin = { x: e.clientX, y: e.clientY };
            this.setState({ transition: fast, opacity: 0.8 });
            this.isMousing = true;
        }
        window.addEventListener('mouseup', this.swiped);
        window.addEventListener('mousemove', this.move);
    },
    move(e) {
        let current;
        if (this.isTouching && e.type === 'touchmove') {
            current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
        } else if (this.isMousing && e.type === 'mousemove') {
            current = { x: e.clientX, y: e.clientY };
        } else {
            return;
        }
        if (!this.isScrolling && !this.isDragging) {
            const xDistance =
                Math.abs(current.x - this.origin.x) - Math.abs(current.y - this.origin.y);
            const isHorizontal = xDistance > 0;
            if (isHorizontal) {
                this.isScrolling = false;
                this.isDragging = true;
            } else {
                this.isScrolling = true;
                this.isDragging = false;
                this.setState({ right: 0, transition: smooth, opacity: 1 });
            }
        }
        if (this.isDragging) {
            e.preventDefault();
            const delta = current.x - this.origin.x;
            this.setState({ right: delta, opacity: (1 - Math.abs(delta) / 400) - 0.2 });
        }
    },
    clicked(e) {
        if (this.isScrolling) return;
        e.preventDefault();
    },
    swiped(e) {
        if (!this.isScrolling) {
            e.preventDefault();
            if (this.state.right > 50) {
                this.add();
                this.setState({ right: 400, transition: smooth, opacity: 0 });
            } else if (this.state.right < -50) {
                this.drop();
                this.setState({ right: -400, transition: smooth, opacity: 0 });
            } else {
                this.setState({ right: 0, transition: smooth, opacity: 1 });
            }
        }

        this.origin = 0;
        this.isMousing = false;
        this.isTouching = false;
        this.isScrolling = false;
        this.isDragging = false;
        window.removeEventListener('mousemove', this.move);
        window.removeEventListener('mouseup', this.swiped);
    },
    render() {
        const { post, style, add, drop, show = false } = this.props;
        this.add = add;
        this.drop = drop;
        const { right, transition, opacity } = this.state;
        const sourceProps = pick(post, 'source', 'sourceImage', 'sourceUrl', 'title', 'added');

        return (
            <div
                onMouseDown={this.start}
                onClick={this.clicked}
                onTouchMove={this.move}
                onTouchEnd={this.swiped}
                onTouchStart={this.start}
                className={classes('relative', 'mt2', 'z-depth-2', 'roboto', 'white-text', 'bg-grey-800', 'br1')}
                style={assign({
                    transform: show ? `translateX(${right}px)` : 'translate(-400px)',
                    transition,
                    opacity: show ? opacity : 0,
                }, omit(style, 'opacity', 'height'))}
            >

                            {post.title}
                            <SiteAttribution {...sourceProps} />

        );
    },
});

export default equip(NewsCard);
<commit_msg>Fixed bug with not being able to click on a card.<commit_after>import assign from 'lodash/assign';
import pick from 'lodash/pick';
import omit from 'lodash/omit';
import React from 'react';
import classes from 'classnames';
import equip from './equip';
import SiteAttribution from './site-attribution';

const smooth = 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms';
const fast = '';

const NewsCard = React.createClass({
    getInitialState() {
        return { right: 0, transition: smooth, opacity: 1 };
    },
    start(e) {
        if (!this.isTouching && !this.isMousing && e.type === 'touchstart') {
            this.origin = { x: e.touches[0].clientX, y: e.touches[0].clientY };
            this.setState({ transition: fast, opacity: 0.8 });
            this.isTouching = true;
        }
        if (!this.isTouching && !this.isMousing && e.type === 'mousedown') {
            this.origin = { x: e.clientX, y: e.clientY };
            this.setState({ transition: fast, opacity: 0.8 });
            this.isMousing = true;
        }
        window.addEventListener('mouseup', this.swiped);
        window.addEventListener('mousemove', this.move);
    },
    move(e) {
        let current;
        if (this.isTouching && e.type === 'touchmove') {
            current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
        } else if (this.isMousing && e.type === 'mousemove') {
            current = { x: e.clientX, y: e.clientY };
        } else {
            return;
        }
        if (!this.isScrolling && !this.isDragging) {
            const xDistance =
                Math.abs(current.x - this.origin.x) - Math.abs(current.y - this.origin.y);
            const isHorizontal = xDistance > 0;
            if (isHorizontal) {
                this.isScrolling = false;
                this.isDragging = true;
            } else {
                this.isScrolling = true;
                this.isDragging = false;
                this.setState({ right: 0, transition: smooth, opacity: 1 });
            }
        }
        if (this.isDragging) {
            e.preventDefault();
            const delta = current.x - this.origin.x;
            this.setState({ right: delta, opacity: (1 - Math.abs(delta) / 400) - 0.2 });
        }
    },
    clicked(e) {
        if (!this.wasDragging) return;
        e.preventDefault();
    },
    swiped(e) {
        if (!this.isScrolling) {
            e.preventDefault();
            if (this.state.right > 50) {
                this.add();
                this.setState({ right: 400, transition: smooth, opacity: 0 });
            } else if (this.state.right < -50) {
                this.drop();
                this.setState({ right: -400, transition: smooth, opacity: 0 });
            } else {
                this.setState({ right: 0, transition: smooth, opacity: 1 });
            }
        }

        this.wasDragging = this.isDragging;
        this.origin = 0;
        this.isMousing = false;
        this.isTouching = false;
        this.isScrolling = false;
        this.isDragging = false;
        window.removeEventListener('mousemove', this.move);
        window.removeEventListener('mouseup', this.swiped);
    },
    render() {
        const { post, style, add, drop, show = false } = this.props;
        this.add = add;
        this.drop = drop;
        const { right, transition, opacity } = this.state;
        const sourceProps = pick(post, 'source', 'sourceImage', 'sourceUrl', 'title', 'added');

        return (
            <div
                onMouseDown={this.start}
                onClick={this.clicked}
                onTouchMove={this.move}
                onTouchEnd={this.swiped}
                onTouchStart={this.start}
                className={classes('relative', 'mt2', 'z-depth-2', 'roboto', 'white-text', 'bg-grey-800', 'br1')}
                style={assign({
                    transform: show ? `translateX(${right}px)` : 'translate(-400px)',
                    transition,
                    opacity: show ? opacity : 0,
                }, omit(style, 'opacity', 'height'))}
            >

                            {post.title}
                            <SiteAttribution {...sourceProps} />

        );
    },
});

export default equip(NewsCard);
<|endoftext|>


<commit_before>'use strict';

var Vue = require('vue');
var WeeksPlusDays = require('./WeeksPlusDays.js');
var Percentage = require('./Percentage.js');
var CurrentMonth = require('./CurrentMonth.js');
var Size = require('./Size.js');
var Weight = require('./Weight.js');

var Timer = require('./timer.js')();
var timer = new Timer();
var day = timer.currentDayFromProjectedDate(timer.extractDateFromUrl(window.location.hash), Date.now());

Vue.component('weeks-plus-days', WeeksPlusDays);
Vue.component('percentage', Percentage);
Vue.component('current-month', CurrentMonth);
Vue.component('size', Size);
Vue.component('weight', Weight);

var app = new Vue({
    el: '#app',
    data: {
        day: day,
        displaySettings: false,
        visibleMetrics: [
            'CurrentMonth',
            'WeeksPlusDays',
            'Percentage',
            'Size',
            'Weight'
        ]
    },
    methods: {
        toggleSettings: function () {
            this.displaySettings = !this.displaySettings;
        }
    }
});
<commit_msg>Persist settings<commit_after>'use strict';

var Vue = require('vue');
var WeeksPlusDays = require('./WeeksPlusDays.js');
var Percentage = require('./Percentage.js');
var CurrentMonth = require('./CurrentMonth.js');
var Size = require('./Size.js');
var Weight = require('./Weight.js');

var Timer = require('./timer.js')();
var timer = new Timer();
var day = timer.currentDayFromProjectedDate(timer.extractDateFromUrl(window.location.hash), Date.now());

Vue.component('weeks-plus-days', WeeksPlusDays);
Vue.component('percentage', Percentage);
Vue.component('current-month', CurrentMonth);
Vue.component('size', Size);
Vue.component('weight', Weight);

var visibleMetrics = JSON.parse(localStorage.getItem('visibleMetrics')) || [
    'CurrentMonth',
    'WeeksPlusDays',
    'Percentage',
    'Size',
    'Weight'
];

var app = new Vue({
    el: '#app',
    data: {
        day: day,
        displaySettings: false,
        visibleMetrics: visibleMetrics
    },
    methods: {
        toggleSettings: function () {
            this.displaySettings = !this.displaySettings;
        }
    },
    watch: {
        visibleMetrics: function (newValue) {
            if (localStorage) {
                localStorage.setItem('visibleMetrics', JSON.stringify(newValue));
            }
        }
    }
});
<|endoftext|>


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

var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');

var app = express();

// all environments
app.set('port', process.env.PORT || 4000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(express.bodyParser());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

app.get('/', user.list);
http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});
<commit_msg>test commit<commit_after>
/**
 * Module dependencies.
 * Express > 4.0
 *
 */

var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');

var app = express();

// all environments
app.set('port', process.env.PORT || 4000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(express.bodyParser());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

app.get('/', user.list);
http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});
<|endoftext|>


<commit_before>// Contact

import React from 'react/addons';
import { Grid, Row, Col } from 'react-bootstrap';
import styles from './Contact.less';
import withStyles from '../../decorators/withStyles';
import { staticPath } from '../../utilities/static/StaticPath';
import { supportsSVG, isIE10OrOlder } from '../../utilities/FeatureDetection/FeatureDetection';
import $ from 'jquery';
const request = require('superagent');

@withStyles(styles)
export default class Contact {
  render() {
    const socialMedia = [
      {
        title: 'Behance',
        url: 'https://www.behance.net/tonikarttunen',
        imageFilename: 'behance'
      },
      {
        title: 'GitHub',
        url: 'https://github.com/tonikarttunen',
        imageFilename: 'github'
      },
      {
        title: 'Medium',
        url: 'https://medium.com/@tonikarttunen',
        imageFilename: 'medium'
      },
      {
        title: 'Twitter',
        url: 'https://twitter.com/tonikarttunen',
        imageFilename: 'twitter'
      },
      {
        title: 'Instagram',
        url: 'http://instagram.com/tonikarttunen',
        imageFilename: 'instagram'
      },
      {
        title: '500px',
        url: 'http://500px.com/tonikarttunen',
        imageFilename: '500px'
      }
    ];

    const socialMediaImageBasePath = 'src/components/Contact/images/';

    return (
      <Grid componentClass='section' className='Contact'>
        <Row>
          {
            socialMedia.map(
              (media) => {
                return (

                    <Col md={3} sm={6} className='ContactRow'>

                        {
                          (() => {
                            if (supportsSVG() && !isIE10OrOlder()) {
                              request
                              .get(staticPath(socialMediaImageBasePath + 'SVG/' + media.imageFilename + '.svg'))
                              .end((err, res) => {
                                if (!err) {
                                  $('#ContactSocialMedia' + media.title).html(res.text);
                                }
                              });
                            } else {
                              return (

                              )
                            }
                          })()
                        }

                        {media.title}

                    </Col>

                );
              }
            )
          }
        </Row>
      </Grid>
    );
  }
}
<commit_msg>Move HTTP request to componentDidMount in Contact component<commit_after>// Contact

import React from 'react/addons';
import { Grid, Row, Col } from 'react-bootstrap';
import styles from './Contact.less';
import withStyles from '../../decorators/withStyles';
import { staticPath } from '../../utilities/static/StaticPath';
import { supportsSVG, isIE10OrOlder } from '../../utilities/FeatureDetection/FeatureDetection';
import $ from 'jquery';
const request = require('superagent');

const socialMediaImageBasePath = 'src/components/Contact/images/';

const socialMedia = [
  {
    title: 'Behance',
    url: 'https://www.behance.net/tonikarttunen',
    imageFilename: 'behance'
  },
  {
    title: 'GitHub',
    url: 'https://github.com/tonikarttunen',
    imageFilename: 'github'
  },
  {
    title: 'Medium',
    url: 'https://medium.com/@tonikarttunen',
    imageFilename: 'medium'
  },
  {
    title: 'Twitter',
    url: 'https://twitter.com/tonikarttunen',
    imageFilename: 'twitter'
  },
  {
    title: 'Instagram',
    url: 'http://instagram.com/tonikarttunen',
    imageFilename: 'instagram'
  },
  {
    title: '500px',
    url: 'http://500px.com/tonikarttunen',
    imageFilename: '500px'
  }
];

@withStyles(styles)
export default class Contact extends React.Component {
  componentDidMount() {
    if (supportsSVG() && !isIE10OrOlder()) {
      socialMedia.map(media => {
        request
        .get(staticPath(socialMediaImageBasePath + 'SVG/' + media.imageFilename + '.svg'))
        .end((err, res) => {
          if (!err) {
            $('#ContactSocialMedia' + media.title).html(res.text);
          }
        });
      });
    }
  }

  render() {
    return (
      <Grid componentClass='section' className='Contact'>
        <Row>
          {
            socialMedia.map(media => {
              return (

                  <Col md={3} sm={6} className='ContactRow'>

                      {
                        (() => {
                          if (!supportsSVG() || isIE10OrOlder()) {
                            return (

                            )
                          }
                        })()
                      }

                      {media.title}

                  </Col>

              );
            })
          }
        </Row>
      </Grid>
    );
  }
}
<|endoftext|>


<commit_before>var handlebars = require('handlebars'),
	read = require('fs').readFileSync,
  swag = require('./test/minimal/swagger.json');

var config = {
    'assertionFormat':'should',
    'paths':['/', '/user']
  };

handlebars.registerHelper('compare', function(lvalue, rvalue, options) {

    if (arguments.length < 3)
      throw new Error("Handlerbars Helper 'compare' needs 2 parameters");

    var operator = options.hash.operator || "==";

    var operators = {
      '==':   function(l,r) { return l == r; },
      '===':  function(l,r) { return l === r; },
      '!=':   function(l,r) { return l != r; },
      '<':    function(l,r) { return l < r; },
      '>':    function(l,r) { return l > r; },
      '<=':   function(l,r) { return l <= r; },
      '>=':   function(l,r) { return l >= r; },
      'typeof': function(l,r) { return typeof l == r; }
    }

    if (!operators[operator])
      throw new Error("Handlerbars Helper 'compare' doesn't know the operator "+operator);

    var result = operators[operator](lvalue,rvalue);

    if( result ) {
      return options.fn(this);
    } else {
      return options.inverse(this);
    }

  });

function testGenResponse(swagger, path, operation, response, config){
  var result, gen, source,
    // request payload
    data = {
      'path':path,
      'responseCode':response,
      'description':swagger.paths[path][operation]['responses'][response].description,
      'assertion':config.assertionFormat
    };

  // adding body parameters to payload
  if (swagger.paths[path][operation].hasOwnProperty('parameters')){
    data.parameters = [];

    // only adds body parameters to request
    for (var param in swagger.paths[path][operation]['parameters'])
      if (swagger.paths[path][operation]['parameters'][param].in == 'body')
        data.parameters.push(swagger.paths[path][operation]['parameters'][param]);
  }

  // template source decision logic
  if (operation == 'get'){
    if (!data.hasOwnProperty('parameters') || data.parameters.length == 0){
      source = read('./templates/supertest/get/get.handlebars','utf8');
    }
  }
  else if (operation == 'post'){
    source = read('./templates/supertest/post/post.handlebars', 'utf8');
  }
  else if (operation == 'put')
    console.log("----------- "+operation+" ------------")
  else if (operation == 'delete')
    console.log("----------- "+operation+" ------------")

  // compile template source and return test string
  gen = handlebars.compile(source);
  result = gen(data);

  return result;
}

function testGenOperation(swagger, path, operation, config){
  var responses = swagger.paths[path][operation]['responses'],
    result = [];

  for (res in responses){
    result.push(testGenResponse(swagger,
      path,
      operation,
      res,
      config));
  }

  var output = "describe('"+operation+"', function(){\n";
  for (test in result)
    output+=result[test]

  output+="});\n"

  return output;
}

function testGenPath(swagger, path, config){
  var operations = swagger.paths[path],
    result = [];

  for (op in operations){
    result.push(testGenOperation(swagger, path, op, config));
  }

  var output = "describe('"+path+"', function(){\n";
  for (test in result)
    output+=result[test]

  output+="});\n";

  return output;
}

function testGen(swagger, config){
	var paths = swagger['paths'],
    targets = config.paths,
		result = [];

  if (config.paths.length == 0)
    for (var path in paths)
      result.push(testGenPath(swagger, path, config));

	//loops over specified paths
	for (var path in targets)
    if (paths.hasOwnProperty(targets[path]))
      result.push(testGenPath(swagger, targets[path], config));

  var output = "describe('"+swagger.info.title+"', function(){\n";
  for (test in result)
    output+=result[test]

  output+="});\n";

  console.log(output);
  return output;
}

testGen(swag, config);<commit_msg>added comments<commit_after>var handlebars = require('handlebars'),
	read = require('fs').readFileSync,
  swag = require('./test/minimal/swagger.json');

var config = {
    'assertionFormat':'should',
    'paths':['/', '/user']
  };

/**
 * Builds a unit test stubs for the response code of a path's operation
 * @param  {json}
 * @param  {string}
 * @param  {string}
 * @param  {string}
 * @param  {json}
 * @return {string}
 */
function testGenResponse(swagger, path, operation, response, config){
  var result, gen, source,
    // request payload
    data = {
      'path':path,
      'responseCode':response,
      'description':swagger.paths[path][operation]['responses'][response].description,
      'assertion':config.assertionFormat
    };

  // adding body parameters to payload
  if (swagger.paths[path][operation].hasOwnProperty('parameters')){
    data.parameters = [];

    // only adds body parameters to request
    for (var param in swagger.paths[path][operation]['parameters'])
      if (swagger.paths[path][operation]['parameters'][param].in == 'body')
        data.parameters.push(swagger.paths[path][operation]['parameters'][param]);
  }

  // template source decision logic
  if (operation == 'get'){
    if (!data.hasOwnProperty('parameters') || data.parameters.length == 0){
      source = read('./templates/supertest/get/get.handlebars','utf8');
    }
  }
  else if (operation == 'post'){
    source = read('./templates/supertest/post/post.handlebars', 'utf8');
  }
  else if (operation == 'put')
    console.log("----------- "+operation+" ------------")
  else if (operation == 'delete')
    console.log("----------- "+operation+" ------------")

  // compile template source and return test string
  gen = handlebars.compile(source);
  result = gen(data);

  return result;
}

/**
 * Builds a set of unit test stubsfor all response codes of a
 *  path's operation
 * @param  {json}
 * @param  {string}
 * @param  {string}
 * @param  {json}
 * @return {string}
 */
function testGenOperation(swagger, path, operation, config){
  var responses = swagger.paths[path][operation]['responses'],
    result = [];

  for (res in responses){
    result.push(testGenResponse(swagger,
      path,
      operation,
      res,
      config));
  }

  var output = "describe('"+operation+"', function(){\n";
  for (test in result)
    output+=result[test]

  output+="});\n"

  return output;
}

/**
 * Builds a set of unit test stubs for all of a path's operations
 * @param  {json}
 * @param  {string}
 * @param  {json}
 * @return {string}
 */
function testGenPath(swagger, path, config){
  var operations = swagger.paths[path],
    result = [];

  for (op in operations){
    result.push(testGenOperation(swagger, path, op, config));
  }

  var output = "describe('"+path+"', function(){\n";
  for (test in result)
    output+=result[test]

  output+="});\n";

  return output;
}

/**
 * Builds unit test stubs for all paths specified by the configuration
 * @param  {json}
 * @param  {json}
 * @return {string}
 */
function testGen(swagger, config){
	var paths = swagger['paths'],
    targets = config.paths,
		result = [];

  if (config.paths.length == 0)
    for (var path in paths)
      result.push(testGenPath(swagger, path, config));

	//loops over specified paths
	for (var path in targets)
    if (paths.hasOwnProperty(targets[path]))
      result.push(testGenPath(swagger, targets[path], config));

  var output = "describe('"+swagger.info.title+"', function(){\n";
  for (test in result)
    output+=result[test]

  output+="});\n";

  console.log(output);
  return output;
}

handlebars.registerHelper('compare', function(lvalue, rvalue, options) {

    if (arguments.length < 3)
      throw new Error("Handlerbars Helper 'compare' needs 2 parameters");

    var operator = options.hash.operator || "==";

    var operators = {
      '==':   function(l,r) { return l == r; },
      '===':  function(l,r) { return l === r; },
      '!=':   function(l,r) { return l != r; },
      '<':    function(l,r) { return l < r; },
      '>':    function(l,r) { return l > r; },
      '<=':   function(l,r) { return l <= r; },
      '>=':   function(l,r) { return l >= r; },
      'typeof': function(l,r) { return typeof l == r; }
    }

    if (!operators[operator])
      throw new Error("Handlerbars Helper 'compare' doesn't know the operator "+operator);

    var result = operators[operator](lvalue,rvalue);

    if( result ) {
      return options.fn(this);
    } else {
      return options.inverse(this);
    }

  });

testGen(swag, config);<|endoftext|>


<commit_before>// jshint bitwise: false
// jshint camelcase: false

(function (window, document) {
  'use strict';

  var options = {
    speed: 3,
    trail: 0.013,
    ammount: 3000,
    collision: true,
    image: 'the-bathers',
    running: true,
    zoom: 1
  };

  if (window.location.hash === '#webcam') {
    options.image = 'use your webcam';
  }

  var width = 533;
  var height = 400;

  var canvas = document.querySelector('canvas');
  var context = canvas.getContext('2d');

  var _canvas = document.createElement('canvas');
  var _context = _canvas.getContext('2d');

  var preview = document.querySelector('.preview');
  var video = document.querySelector('video');
  var webcam = document.querySelector('.webcam');

  var particles = [];
  var data = [];

  var gui = new dat.GUI(); // jshint ignore: line
  gui.add(options, 'speed', 1, 10);
  var trailSelect = gui.add(options, 'trail', 0.01, 0.5);
  var ammountSelect = gui.add(options, 'ammount', 1, 15000).step(1);
  var imageSelect = gui.add(options, 'image', [
    'use your webcam',
    'the-bathers',
    'at-the-moulin-rouge',
    'the-starry-night',
    'senecio',
    'mother-and-child',
    'still-life-with-a-guitar'
  ]);
  var zoomSelect = gui.add(options, 'zoom', {
    '25%': 0.25,
    '50%': 0.5,
    '100%': 1,
    '150%': 1.5,
    '200%': 2,
  });
  gui.add(options, 'collision');
  gui.add(options, 'running');

  var stats = new Stats(); // jshint ignore: line
  stats.domElement.style.position = 'fixed';
  stats.domElement.style.right = '0px';
  stats.domElement.style.bottom = '0px';
  document.body.appendChild(stats.domElement);

  function fadeCanvas() {
    context.fillRect(0, 0, width, height);
  }

  function renderImage(image, preventClear) {
    var id;

    width = image.width * options.zoom;
    height = image.height * options.zoom;

    if (preventClear) {
      _canvas.width = width;
      _canvas.height = height;
      _context.drawImage(image, 0, 0, width, height);
      id = _context.getImageData(0, 0, width, height).data;
    } else {
      canvas.width = width;
      canvas.height = height;
      context.drawImage(image, 0, 0, width, height);
      id = context.getImageData(0, 0, width, height).data;
    }

    var l = id.length >> 2;
    var d = new Uint16Array(l);
    for (var i = 0, j = 0; i < l; i++, j += 4) {
      d[i] = ((id[j] >> 3) << 10) | ((id[j+1] >> 3) <<5) | (id[j+2] >> 3);
    }
    if (preventClear) {
      _context.clearRect(0, 0, width, height);
    } else {
      context.clearRect(0, 0, width, height);
    }
    data = d;
  }

  function createParticle() {
    var x = Math.random() * width;
    var y = Math.random() * height;

    return {
      lastX: x,
      lastY: y,
      x: x,
      y: y,
      vx: Math.random() - 0.5,
      vy: Math.random() - 0.5
    };
  }

  function moveParticle(p) {
    var lastX = p.x;
    var lastY = p.y;

    var vx = p.vx;
    var vy = p.vy;

    var x = lastX + p.vx * options.speed;
    var y = lastY + p.vy * options.speed;

    if (x > width) {
      x = width;
      vx = -vx;
    } else if (x < 0) {
      x = 0;
      vx = -vx;
    }

    if (y > height) {
      y = height;
      vy = -vy;
    } else if (y < 0) {
      y = 0;
      vy = -vy;
    }

    return {
      lastX: lastX,
      lastY: lastY,
      x: x,
      y: y,
      vx: vx,
      vy: vy,
    };
  }

  function linearPosition(p) {
    return p.y * width + p.x;
  }

  var scaleSystem = function (count) {
    var l  = particles.length;

    if (count < l) {
      while (--l) {
        var j = Math.random() * l >> 0;
        var tmp = particles[l];
        particles[l] = particles[j];
        particles[j] = tmp;
      }
      particles = particles.slice(0, count);
    } else if (count > l) {
      for (var i = l; i < count; i++) {
        particles.push(createParticle());
      }
    }
  };

  function findAnAngle(vx, vy) {
    if (vy > 0) {
      return Math.atan2(vy, vx);
    } else {
      return Math.PI + Math.PI + Math.atan2(vy, vx);
    }
  }

  function checkCollision (a, b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    if (dx * dx + dy * dy <= 1) {
      var phi;

      if (dx === 0) {
        phi = Math.PI / 2;
      } else {
        phi = Math.atan2(dy, dx);
      }

      var aang = findAnAngle(a.vx, a.vy);
      var bang = findAnAngle(b.vx, b.vy);

      var v1 = Math.sqrt(a.vx * a.vx + a.vy * a.vy);
      var v2 = Math.sqrt(b.vx * b.vx + b.vy * b.vy);

      var s_phi = Math.sin(phi);
      var c_phi = Math.cos(phi);
      var s_a_phi = Math.sin(aang-phi);
      var s_b_phi = Math.sin(bang-phi);
      var c_a_phi = Math.cos(aang-phi);
      var c_b_phi = Math.cos(bang-phi);
      var s_phi_pi2 = Math.sin(phi+Math.PI / 2);
      var c_phi_pi2 = Math.cos(phi+Math.PI / 2);

      a.vx = v2 * c_b_phi * c_phi + v1 * s_a_phi * c_phi_pi2;
      a.vy = v2 * c_b_phi * s_phi + v1 * s_a_phi * s_phi_pi2;
      b.vx = v1 * c_a_phi * c_phi + v2 * s_b_phi * c_phi_pi2;
      b.vy = v1 * c_a_phi * s_phi + v2 * s_b_phi * s_phi_pi2;
    }
  }

  function loadImage(image) {
    if (image === 'use your webcam') {
      canvas.width = video.width;
      canvas.height = video.height;

      preview.style.display = 'none';
      video.style.display = '';

      navigator.getUserMedia = ( navigator.getUserMedia ||
                                navigator.webkitGetUserMedia ||
                                navigator.mozGetUserMedia ||
                                navigator.msGetUserMedia);
      navigator.getUserMedia({ video: true }, function (localMediaStream) {
        var video = document.querySelector('video');
        video.src = window.URL.createObjectURL(localMediaStream);
      }, function(err) { console.error(err); });

      return;
    } else {
      preview.style.display = '';
      video.style.display = 'none';
    }

    function onload() {
      renderImage(preview);
      trailChanged(options.trail);
    }
    preview.src = 'images/' + image + '.jpg';

    if (preview.complete) {
      onload();
    } else {
      preview.onload = onload;
    }
  }

  function trailChanged(trail) {
    context.fillStyle = 'rgba(0,0,0,'+trail+')';
  }

  trailSelect.onChange(trailChanged);
  imageSelect.onChange(loadImage);
  ammountSelect.onChange(scaleSystem);
  zoomSelect.onChange(loadImage);

  trailChanged(options.trail);
  scaleSystem(options.ammount);
  loadImage(options.image);

  function render() {
    if (options.running) {
      stats.begin();

      if (options.image === 'use your webcam' && video.currentTime > 1) {
        renderImage(video, true);
        trailChanged(options.trail);
      }

      fadeCanvas();

      if (options.collision) {
        particles = particles.sort(function (pa, pb) {
          return linearPosition(pa) - linearPosition(pb);
        });
      }

      var draws = {};
      for (var i = 0, l = particles.length; i < l; i++) {
        var pa = particles[i];
        var pb = particles[i+1];

        if (pb && options.collision) {
          checkCollision(pa, pb);
        }

        var p = moveParticle(pa);
        particles[i] = p;

        var index = ((p.y>>0)*width)+(p.x>>0);

        var x = data[index];
        if ('undefined' === typeof draws[x]) {
          draws[x] = [];
        }
        draws[x].push(p);
      }

      var colors = Object.keys(draws);
      for (var y = 0, ll = colors.length; y < ll; y++) {
        var color = colors[y];
        var ps = draws[color];

        var r = color >> 10;
        var g = (color >> 5) & 31;
        var b = color & 31;

        var c = 'rgb('+r * 8+', '+g * 8+', '+b*8+')';
        context.strokeStyle = c;
        context.beginPath();
        for (var j = 0, len = ps.length; j < len; j++) {
          var particle = ps[j];
          context.moveTo(particle.lastX, particle.lastY);
          context.lineTo(particle.x, particle.y);
        }
        context.stroke();
      }

      stats.end();
    }

    window.requestAnimationFrame(render);
  }

  webcam.onclick = function (e) {
    e.preventDefault();
    options.image = 'use your webcam';
    loadImage(options.image);
    imageSelect.updateDisplay();
  };

  window.addEventListener('DOMContentLoaded', function () {
    window.requestAnimationFrame(render);
  }, false);
})(window, document);
<commit_msg>Hack different solution for firefox<commit_after>// jshint bitwise: false
// jshint camelcase: false

(function (window, document) {
  'use strict';

  var options = {
    speed: 3,
    trail: 0.013,
    ammount: 3000,
    collision: true,
    image: 'the-bathers',
    running: true,
    zoom: 1
  };

  if (window.location.hash === '#webcam') {
    options.image = 'use your webcam';
  }

  var width = 533;
  var height = 400;

  var canvas = document.querySelector('canvas');
  var context = canvas.getContext('2d');

  var _canvas = document.createElement('canvas');
  var _context = _canvas.getContext('2d');

  var preview = document.querySelector('.preview');
  var video = document.querySelector('video');
  var webcam = document.querySelector('.webcam');

  var isVideo = false;
  var isFirefox = /firefox/i.test(navigator.userAgent);
  var timeout = isFirefox ? 1000 : 0;

  var particles = [];
  var data = [];

  var gui = new dat.GUI(); // jshint ignore: line
  gui.add(options, 'speed', 1, 10);
  var trailSelect = gui.add(options, 'trail', 0.01, 0.5);
  var ammountSelect = gui.add(options, 'ammount', 1, 15000).step(1);
  var imageSelect = gui.add(options, 'image', [
    'use your webcam',
    'the-bathers',
    'at-the-moulin-rouge',
    'the-starry-night',
    'senecio',
    'mother-and-child',
    'still-life-with-a-guitar'
  ]);
  var zoomSelect = gui.add(options, 'zoom', {
    '25%': 0.25,
    '50%': 0.5,
    '100%': 1,
    '150%': 1.5,
    '200%': 2,
  });
  gui.add(options, 'collision');
  gui.add(options, 'running');

  var stats = new Stats(); // jshint ignore: line
  stats.domElement.style.position = 'fixed';
  stats.domElement.style.right = '0px';
  stats.domElement.style.bottom = '0px';
  document.body.appendChild(stats.domElement);

  function fadeCanvas() {
    context.fillRect(0, 0, width, height);
  }

  function renderImage(image, preventClear) {
    var id;

    width = image.width * options.zoom;
    height = image.height * options.zoom;

    if (preventClear) {
      _canvas.width = width;
      _canvas.height = height;
      _context.drawImage(image, 0, 0, width, height);
      id = _context.getImageData(0, 0, width, height).data;
    } else {
      canvas.width = width;
      canvas.height = height;
      context.drawImage(image, 0, 0, width, height);
      id = context.getImageData(0, 0, width, height).data;
    }

    var l = id.length >> 2;
    var d = new Uint16Array(l);
    for (var i = 0, j = 0; i < l; i++, j += 4) {
      d[i] = ((id[j] >> 3) << 10) | ((id[j+1] >> 3) <<5) | (id[j+2] >> 3);
    }
    if (preventClear) {
      _context.clearRect(0, 0, width, height);
    } else {
      context.clearRect(0, 0, width, height);
    }
    data = d;
  }

  function createParticle() {
    var x = Math.random() * width;
    var y = Math.random() * height;

    return {
      lastX: x,
      lastY: y,
      x: x,
      y: y,
      vx: Math.random() - 0.5,
      vy: Math.random() - 0.5
    };
  }

  function moveParticle(p) {
    var lastX = p.x;
    var lastY = p.y;

    var vx = p.vx;
    var vy = p.vy;

    var x = lastX + p.vx * options.speed;
    var y = lastY + p.vy * options.speed;

    if (x > width) {
      x = width;
      vx = -vx;
    } else if (x < 0) {
      x = 0;
      vx = -vx;
    }

    if (y > height) {
      y = height;
      vy = -vy;
    } else if (y < 0) {
      y = 0;
      vy = -vy;
    }

    return {
      lastX: lastX,
      lastY: lastY,
      x: x,
      y: y,
      vx: vx,
      vy: vy,
    };
  }

  function linearPosition(p) {
    return p.y * width + p.x;
  }

  var scaleSystem = function (count) {
    var l  = particles.length;

    if (count < l) {
      while (--l) {
        var j = Math.random() * l >> 0;
        var tmp = particles[l];
        particles[l] = particles[j];
        particles[j] = tmp;
      }
      particles = particles.slice(0, count);
    } else if (count > l) {
      for (var i = l; i < count; i++) {
        particles.push(createParticle());
      }
    }
  };

  function findAnAngle(vx, vy) {
    if (vy > 0) {
      return Math.atan2(vy, vx);
    } else {
      return Math.PI + Math.PI + Math.atan2(vy, vx);
    }
  }

  function checkCollision (a, b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    if (dx * dx + dy * dy <= 1) {
      var phi;

      if (dx === 0) {
        phi = Math.PI / 2;
      } else {
        phi = Math.atan2(dy, dx);
      }

      var aang = findAnAngle(a.vx, a.vy);
      var bang = findAnAngle(b.vx, b.vy);

      var v1 = Math.sqrt(a.vx * a.vx + a.vy * a.vy);
      var v2 = Math.sqrt(b.vx * b.vx + b.vy * b.vy);

      var s_phi = Math.sin(phi);
      var c_phi = Math.cos(phi);
      var s_a_phi = Math.sin(aang-phi);
      var s_b_phi = Math.sin(bang-phi);
      var c_a_phi = Math.cos(aang-phi);
      var c_b_phi = Math.cos(bang-phi);
      var s_phi_pi2 = Math.sin(phi+Math.PI / 2);
      var c_phi_pi2 = Math.cos(phi+Math.PI / 2);

      a.vx = v2 * c_b_phi * c_phi + v1 * s_a_phi * c_phi_pi2;
      a.vy = v2 * c_b_phi * s_phi + v1 * s_a_phi * s_phi_pi2;
      b.vx = v1 * c_a_phi * c_phi + v2 * s_b_phi * c_phi_pi2;
      b.vy = v1 * c_a_phi * s_phi + v2 * s_b_phi * s_phi_pi2;
    }
  }

  function loadImage(image) {
    if (image === 'use your webcam') {
      var _running = options.running;

      isVideo = true;
      canvas.width = video.width;
      canvas.height = video.height;
      options.running = false;

      preview.style.display = 'none';
      video.style.display = '';

      navigator.getUserMedia = ( navigator.getUserMedia ||
                                navigator.webkitGetUserMedia ||
                                navigator.mozGetUserMedia ||
                                navigator.msGetUserMedia);
      navigator.getUserMedia({ video: true }, function (localMediaStream) {
        var video = document.querySelector('video');
        video.src = window.URL.createObjectURL(localMediaStream);
        video.onloadedmetadata = function () {
          setTimeout(function () {
            options.running = _running;
          }, timeout);
        };
      }, function(err) { console.error(err); });

      return;
    } else {
      isVideo = false;
      preview.style.display = '';
      video.style.display = 'none';
    }

    function onload() {
      renderImage(preview);
      trailChanged(options.trail);
    }
    preview.src = 'images/' + image + '.jpg';

    if (preview.complete) {
      onload();
    } else {
      preview.onload = onload;
    }
  }

  function trailChanged(trail) {
    context.fillStyle = 'rgba(0,0,0,'+trail+')';
  }

  trailSelect.onChange(trailChanged);
  imageSelect.onChange(loadImage);
  ammountSelect.onChange(scaleSystem);
  zoomSelect.onChange(loadImage);

  trailChanged(options.trail);
  scaleSystem(options.ammount);
  loadImage(options.image);

  function render() {
    if (options.running) {
      stats.begin();

      if (isVideo) {
        renderImage(video, true);
        trailChanged(options.trail);
      }

      fadeCanvas();

      if (options.collision) {
        particles = particles.sort(function (pa, pb) {
          return linearPosition(pa) - linearPosition(pb);
        });
      }

      var draws = {};
      for (var i = 0, l = particles.length; i < l; i++) {
        var pa = particles[i];
        var pb = particles[i+1];

        if (pb && options.collision) {
          checkCollision(pa, pb);
        }

        var p = moveParticle(pa);
        particles[i] = p;

        var index = ((p.y>>0)*width)+(p.x>>0);

        var x = data[index];
        if ('undefined' === typeof draws[x]) {
          draws[x] = [];
        }
        draws[x].push(p);
      }

      var colors = Object.keys(draws);
      for (var y = 0, ll = colors.length; y < ll; y++) {
        var color = colors[y];
        var ps = draws[color];

        var r = color >> 10;
        var g = (color >> 5) & 31;
        var b = color & 31;

        var c = 'rgb('+r * 8+', '+g * 8+', '+b*8+')';
        context.strokeStyle = c;
        context.beginPath();
        for (var j = 0, len = ps.length; j < len; j++) {
          var particle = ps[j];
          context.moveTo(particle.lastX, particle.lastY);
          context.lineTo(particle.x, particle.y);
        }
        context.stroke();
      }

      stats.end();
    }

    window.requestAnimationFrame(render);
  }

  webcam.onclick = function (e) {
    e.preventDefault();
    options.image = 'use your webcam';
    loadImage(options.image);
    imageSelect.updateDisplay();
  };

  window.addEventListener('DOMContentLoaded', function () {
    window.requestAnimationFrame(render);
  }, false);
})(window, document);
<|endoftext|>


<commit_before>'use strict';
var request = require('request');
var express = require('express');
var bodyParser = require('body-parser');
var validations = require('./validations');

function parseMessage(message) {
  try {
    return JSON.parse(message);
  } catch (e) {
    return null;
  }
}

var config = {
  appSecret: process.env.APP_SECRET,
  port: process.env.PORT || 8080
};

if (!config.appSecret) {
  throw new Error('APP_SECRET environment variable is required. See project README for details.');
}

var app = express();
app.use(bodyParser.json());

app.get('/', function (req, res) {
  res.set('Content-Type', 'text/plain');
  res.status(200).send('Respoke sample push server is active!');
});

app.post('/', function (req, res) {
  // immediately acknowledge the request
  res.status(200).end();

  var event = req.body;

  // only process valid push notification events
  if (!validations.isValidPushEvent(event)) {
    return;
  }

  // only process direct messages
  if (event.originalMsg.header.type !== 'message') {
    return;
  }

  // retrieve the original message
  var message = event.originalMsg.body;

  if (event.config.msgProperty) {
    message = parseMessage(message);
    message = _.get(message, event.config.msgProperty) || '';
  }

  // customize the original message
  var bullsEyeEmoji = '🎯';
  message =  bullsEyeEmoji + ' ' + message;

  // dispatch the push notification
  var requestOptions = {
    method: 'POST',
    url: 'https://api-int.respoke.io/v1/push-notification/' + encodeURIComponent(event.pushNotificationId),
    headers: {
      'App-Secret': config.appSecret
    },
    json: {
      originalMsg: event.originalMsg,
      pushText: message
    }
  };

  request(requestOptions, function (err, response) {
    if (err) {
      console.error('error sending push notification:', err);
      return;
    }

    console.log('push notification dispatch response', {
      statusCode: response.statusCode,
      headers: response.headers,
      body: response.body
    });
  });
});

app.listen(config.port, function () {
  console.log('sample push server listening on port ' + config.port);
});
<commit_msg>Fix missing lodash declaration<commit_after>'use strict';
var request = require('request');
var express = require('express');
var bodyParser = require('body-parser');
var validations = require('./validations');
var _ = require('lodash');

function parseMessage(message) {
  try {
    return JSON.parse(message);
  } catch (e) {
    return null;
  }
}

var config = {
  appSecret: process.env.APP_SECRET,
  port: process.env.PORT || 8080
};

if (!config.appSecret) {
  throw new Error('APP_SECRET environment variable is required. See project README for details.');
}

var app = express();
app.use(bodyParser.json());

app.get('/', function (req, res) {
  res.set('Content-Type', 'text/plain');
  res.status(200).send('Respoke sample push server is active!');
});

app.post('/', function (req, res) {
  // immediately acknowledge the request
  res.status(200).end();

  var event = req.body;

  // only process valid push notification events
  if (!validations.isValidPushEvent(event)) {
    return;
  }

  // only process direct messages
  if (event.originalMsg.header.type !== 'message') {
    return;
  }

  // retrieve the original message
  var message = event.originalMsg.body;

  if (event.config.msgProperty) {
    message = parseMessage(message);
    message = _.get(message, event.config.msgProperty) || '';
  }

  // customize the original message
  var bullsEyeEmoji = '🎯';
  message =  bullsEyeEmoji + ' ' + message;

  // dispatch the push notification
  var requestOptions = {
    method: 'POST',
    url: 'https://api-int.respoke.io/v1/push-notification/' + encodeURIComponent(event.pushNotificationId),
    headers: {
      'App-Secret': config.appSecret
    },
    json: {
      originalMsg: event.originalMsg,
      pushText: message
    }
  };

  request(requestOptions, function (err, response) {
    if (err) {
      console.error('error sending push notification:', err);
      return;
    }

    console.log('push notification dispatch response', {
      statusCode: response.statusCode,
      headers: response.headers,
      body: response.body
    });
  });
});

app.listen(config.port, function () {
  console.log('sample push server listening on port ' + config.port);
});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
// import Link from 'react-router';
import Footer from '../Footer/Footer';
import { loadOneIdea } from '../../actions/idea'
import { showLoading, hideLoading } from '../../actions/loading'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import CommentForm from '../Comment/CommentForm'
import ListComments from '../Comment/ListComments'
import ListMembers from '../Comment/ListMembers'
import Auth from '../../helpers/token'
import { addComment } from '../../actions/comment'
import renderHTML from 'react-render-html'
import { Link } from 'react-router'

import {
  convertFromHTML,
  convertToRaw,
  ContentState,
} from 'draft-js';

class IdeaDetail extends Component {
  constructor(props){
    super(props)
  }
  componentDidMount() {
    this.props.loadOneIdea(this.props.params.title)
  }

  render(){
    const { data_idea , addComment } = this.props
    console.log(data_idea.video)
    // let video = data_idea.video.replace(/watch?v=/g, "embed/")

    console.log("render", data_idea);
    if(data_idea.hasOwnProperty('id') === false){
      //loading
      return(

loading bar nih

      )
    }else{
      // this.props.hideLoading()
      console.log('what inside props', data_idea)

      return (

                    {data_idea.title}

Get more insight about the Idea . . .

{data_idea.title}

                         {data_idea.User.name}

                         <Link to="/explore">{data_idea.Category.name}</Link>

                         {data_idea.createdAt}

Details

Status

Comment

Members

                    <button type="button" className="close delete-detail" data-toggle="modal" data-placement="top" title="Delete This Idea" data-target="#deleteModal" aria-label="Delete"></button>
                    <button type="button" className="close edit-detail" data-toggle="tooltip" data-placement="top" title="Edit This Idea"></button>

Featured Video :

Description :

{renderHTML(data_idea.description)}

{data_idea.status}

It means all of the stories about it have just begin

Update

                              <form>

                                  New Update
                                  <select className="form-control">
                                    <option>Baby</option>
                                    <option>Kid</option>
                                    <option>Teen</option>
                                    <option>Mature</option>
                                    <option>RIP</option>
                                  </select>

                                  About this update
                                  <textarea className="form-control" rows={4} placeholder="Tell us something about this update"></textarea>

                                  <button type="submit" className="btn btn-info">Submit</button>

                              </form>

Timeline

                                  18
                                  APR

Kenduigraha

Akhirnya partneran dapet investor!

 11/09/2014

Mussum ipsum cacilds

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

 11/09/2014

Give Comment

                              <CommentForm UserId={Auth.getUser().sub} data_idea={data_idea} addComment={addComment}/>

                      <ListComments data_idea={data_idea} />

                      <ListMembers membersComment={data_idea.Comments} />

                  <button type="button" className="close" data-dismiss="modal" aria-label="Close">×</button>

Delete This Idea?

                  Deleting this idea will permanently remove it along with all of it's comments and update.

                  <button type="button" className="btn btn-default" data-dismiss="modal">No, Keep Idea</button>
                  <button type="button" className="btn btn-danger">Yes, Delete Idea</button>

      )
    }
  }
}

function mapStateToProps(state) {
  return {
    data_idea: state.idea,
    loading: state.loading
  }
}

function mapDispatchToProps(dispatch) {
  return {
    loadOneIdea: bindActionCreators(loadOneIdea, dispatch),
    addComment: bindActionCreators(addComment, dispatch),
    showLoading: bindActionCreators(showLoading, dispatch),
    hideLoading: bindActionCreators(hideLoading, dispatch)
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(IdeaDetail)
<commit_msg>COMMENT CODE STATUS FEATURE<commit_after>import React, { Component } from 'react';
// import Link from 'react-router';
import Footer from '../Footer/Footer';
import { loadOneIdea } from '../../actions/idea'
import { showLoading, hideLoading } from '../../actions/loading'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import CommentForm from '../Comment/CommentForm'
import ListComments from '../Comment/ListComments'
import ListMembers from '../Comment/ListMembers'
import Auth from '../../helpers/token'
import { addComment } from '../../actions/comment'
import renderHTML from 'react-render-html'
import { Link } from 'react-router'

import {
  convertFromHTML,
  convertToRaw,
  ContentState,
} from 'draft-js';

class IdeaDetail extends Component {
  constructor(props){
    super(props)
  }
  componentDidMount() {
    this.props.loadOneIdea(this.props.params.title)
  }

  render(){
    const { data_idea , addComment } = this.props
    console.log(data_idea.video)
    // let video = data_idea.video.replace(/watch?v=/g, "embed/")

    console.log("render", data_idea);
    if(data_idea.hasOwnProperty('id') === false){
      //loading
      return(

loading bar nih

      )
    }else{
      // this.props.hideLoading()
      console.log('what inside props', data_idea)

      return (

                    {data_idea.title}

Get more insight about the Idea . . .

{data_idea.title}

                         {data_idea.User.name}

                         <Link to="/explore">{data_idea.Category.name}</Link>

                         {data_idea.createdAt}

Details

                    {/*
Status
 */}

Comment

Members

                    <button type="button" className="close delete-detail" data-toggle="modal" data-placement="top" title="Delete This Idea" data-target="#deleteModal" aria-label="Delete"></button>
                    <button type="button" className="close edit-detail" data-toggle="tooltip" data-placement="top" title="Edit This Idea"></button>

Featured Video :

Description :

{renderHTML(data_idea.description)}

Give Comment

                              <CommentForm UserId={Auth.getUser().sub} data_idea={data_idea} addComment={addComment}/>

                      <ListComments data_idea={data_idea} />

                      <ListMembers membersComment={data_idea.Comments} />

                  <button type="button" className="close" data-dismiss="modal" aria-label="Close">×</button>

Delete This Idea?

                  Deleting this idea will permanently remove it along with all of it's comments and update.

                  <button type="button" className="btn btn-default" data-dismiss="modal">No, Keep Idea</button>
                  <button type="button" className="btn btn-danger">Yes, Delete Idea</button>

      )
    }
  }
}

function mapStateToProps(state) {
  return {
    data_idea: state.idea,
    loading: state.loading
  }
}

function mapDispatchToProps(dispatch) {
  return {
    loadOneIdea: bindActionCreators(loadOneIdea, dispatch),
    addComment: bindActionCreators(addComment, dispatch),
    showLoading: bindActionCreators(showLoading, dispatch),
    hideLoading: bindActionCreators(hideLoading, dispatch)
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(IdeaDetail)

/*

{data_idea.status}

It means all of the stories about it have just begin

Update

          <form>

              New Update
              <select className="form-control">
                <option>Baby</option>
                <option>Kid</option>
                <option>Teen</option>
                <option>Mature</option>
                <option>RIP</option>
              </select>

              About this update
              <textarea className="form-control" rows={4} placeholder="Tell us something about this update"></textarea>

              <button type="submit" className="btn btn-info">Submit</button>

          </form>

Timeline

              18
              APR

Kenduigraha

Akhirnya partneran dapet investor!

 11/09/2014

Mussum ipsum cacilds

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

 11/09/2014

*/
<|endoftext|>


<commit_before>module.exports = {
	book: {
		assets: "./assets"
		,js: "retina.min.js"
	}
}
<commit_msg>wait is this supposed to be an array?<commit_after>module.exports = {
	book: {
		assets: "./assets"
		,js: ["retina.min.js"]
	}
}
<|endoftext|>