<commit_before>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule processTransform
 * @flow
 */
'use strict';

var MatrixMath = require('MatrixMath');
var Platform = require('Platform');

var invariant = require('invariant');
var stringifySafe = require('stringifySafe');

/**
 * Generate a transform matrix based on the provided transforms, and use that
 * within the style object instead.
 *
 * This allows us to provide an API that is similar to CSS, where transforms may
 * be applied in an arbitrary order, and yet have a universal, singular
 * interface to native code.
 */
function processTransform(transform: Object): Object {
  var result = MatrixMath.createIdentityMatrix();

  transform.forEach(transformation => {
    var key = Object.keys(transformation)[0];
    var value = transformation[key];
    if (__DEV__) {
      _validateTransform(key, value, transformation);
    }

    switch (key) {
      case 'matrix':
        MatrixMath.multiplyInto(result, result, value);
        break;
      case 'perspective':
        _multiplyTransform(result, MatrixMath.reusePerspectiveCommand, [value]);
        break;
      case 'rotateX':
        _multiplyTransform(result, MatrixMath.reuseRotateXCommand, [_convertToRadians(value)]);
        break;
      case 'rotateY':
        _multiplyTransform(result, MatrixMath.reuseRotateYCommand, [_convertToRadians(value)]);
        break;
      case 'rotate':
      case 'rotateZ':
        _multiplyTransform(result, MatrixMath.reuseRotateZCommand, [_convertToRadians(value)]);
        break;
      case 'scale':
        _multiplyTransform(result, MatrixMath.reuseScaleCommand, [value]);
        break;
      case 'scaleX':
        _multiplyTransform(result, MatrixMath.reuseScaleXCommand, [value]);
        break;
      case 'scaleY':
        _multiplyTransform(result, MatrixMath.reuseScaleYCommand, [value]);
        break;
      case 'translate':
        _multiplyTransform(result, MatrixMath.reuseTranslate3dCommand, [value[0], value[1], value[2] || 0]);
        break;
      case 'translateX':
        _multiplyTransform(result, MatrixMath.reuseTranslate2dCommand, [value, 0]);
        break;
      case 'translateY':
        _multiplyTransform(result, MatrixMath.reuseTranslate2dCommand, [0, value]);
        break;
      case 'skewX':
        _multiplyTransform(result, MatrixMath.reuseSkewXCommand, [_convertToRadians(value)]);
        break;
      case 'skewY':
        _multiplyTransform(result, MatrixMath.reuseSkewYCommand, [_convertToRadians(value)]);
        break;
      default:
        throw new Error('Invalid transform name: ' + key);
    }
  });

  // Android does not support the direct application of a transform matrix to
  // a view, so we need to decompose the result matrix into transforms that can
  // get applied in the specific order of (1) translate (2) scale (3) rotate.
  // Once we can directly apply a matrix, we can remove this decomposition.
  if (Platform.OS === 'android') {
    return MatrixMath.decomposeMatrix(result);
  }
  return result;
}

/**
 * Performs a destructive operation on a transform matrix.
 */
function _multiplyTransform(
  result: Array<number>,
  matrixMathFunction: Function,
  args: Array<number>
): void {
  var matrixToApply = MatrixMath.createIdentityMatrix();
  var argsWithIdentity = [matrixToApply].concat(args);
  matrixMathFunction.apply(this, argsWithIdentity);
  MatrixMath.multiplyInto(result, result, matrixToApply);
}

/**
 * Parses a string like '0.5rad' or '60deg' into radians expressed in a float.
 * Note that validation on the string is done in `_validateTransform()`.
 */
function _convertToRadians(value: string): number {
  var floatValue = parseFloat(value, 10);
  return value.indexOf('rad') > -1 ? floatValue : floatValue * Math.PI / 180;
}

function _validateTransform(key, value, transformation) {
  invariant(
    !value.getValue,
    'You passed an Animated.Value to a normal component. ' +
    'You need to wrap that component in an Animated. For example, ' +
    'replace <View /> by <Animated.View />.'
  );

  var multivalueTransforms = [
    'matrix',
    'translate',
  ];
  if (multivalueTransforms.indexOf(key) !== -1) {
    invariant(
      Array.isArray(value),
      'Transform with key of %s must have an array as the value: %s',
      key,
      stringifySafe(transformation),
    );
  }
  switch (key) {
    case 'matrix':
      invariant(
        value.length === 9 || value.length === 16,
        'Matrix transform must have a length of 9 (2d) or 16 (3d). ' +
          'Provided matrix has a length of %s: %s',
        value.length,
        stringifySafe(transformation),
      );
      break;
    case 'translate':
      break;
    case 'rotateX':
    case 'rotateY':
    case 'rotateZ':
    case 'rotate':
    case 'skewX':
    case 'skewY':
      invariant(
        typeof value === 'string',
        'Transform with key of "%s" must be a string: %s',
        key,
        stringifySafe(transformation),
      );
      invariant(
        value.indexOf('deg') > -1 || value.indexOf('rad') > -1,
        'Rotate transform must be expressed in degrees (deg) or radians ' +
          '(rad): %s',
        stringifySafe(transformation),
      );
      break;
    default:
      invariant(
        typeof value === 'number',
        'Transform with key of "%s" must be a number: %s',
        key,
        stringifySafe(transformation),
      );
  }
}

module.exports = processTransform;
<commit_msg>Ensure perspective value is non-zero<commit_after>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule processTransform
 * @flow
 */
'use strict';

var MatrixMath = require('MatrixMath');
var Platform = require('Platform');

var invariant = require('invariant');
var stringifySafe = require('stringifySafe');

/**
 * Generate a transform matrix based on the provided transforms, and use that
 * within the style object instead.
 *
 * This allows us to provide an API that is similar to CSS, where transforms may
 * be applied in an arbitrary order, and yet have a universal, singular
 * interface to native code.
 */
function processTransform(transform: Object): Object {
  var result = MatrixMath.createIdentityMatrix();

  transform.forEach(transformation => {
    var key = Object.keys(transformation)[0];
    var value = transformation[key];
    if (__DEV__) {
      _validateTransform(key, value, transformation);
    }

    switch (key) {
      case 'matrix':
        MatrixMath.multiplyInto(result, result, value);
        break;
      case 'perspective':
        _multiplyTransform(result, MatrixMath.reusePerspectiveCommand, [value]);
        break;
      case 'rotateX':
        _multiplyTransform(result, MatrixMath.reuseRotateXCommand, [_convertToRadians(value)]);
        break;
      case 'rotateY':
        _multiplyTransform(result, MatrixMath.reuseRotateYCommand, [_convertToRadians(value)]);
        break;
      case 'rotate':
      case 'rotateZ':
        _multiplyTransform(result, MatrixMath.reuseRotateZCommand, [_convertToRadians(value)]);
        break;
      case 'scale':
        _multiplyTransform(result, MatrixMath.reuseScaleCommand, [value]);
        break;
      case 'scaleX':
        _multiplyTransform(result, MatrixMath.reuseScaleXCommand, [value]);
        break;
      case 'scaleY':
        _multiplyTransform(result, MatrixMath.reuseScaleYCommand, [value]);
        break;
      case 'translate':
        _multiplyTransform(result, MatrixMath.reuseTranslate3dCommand, [value[0], value[1], value[2] || 0]);
        break;
      case 'translateX':
        _multiplyTransform(result, MatrixMath.reuseTranslate2dCommand, [value, 0]);
        break;
      case 'translateY':
        _multiplyTransform(result, MatrixMath.reuseTranslate2dCommand, [0, value]);
        break;
      case 'skewX':
        _multiplyTransform(result, MatrixMath.reuseSkewXCommand, [_convertToRadians(value)]);
        break;
      case 'skewY':
        _multiplyTransform(result, MatrixMath.reuseSkewYCommand, [_convertToRadians(value)]);
        break;
      default:
        throw new Error('Invalid transform name: ' + key);
    }
  });

  // Android does not support the direct application of a transform matrix to
  // a view, so we need to decompose the result matrix into transforms that can
  // get applied in the specific order of (1) translate (2) scale (3) rotate.
  // Once we can directly apply a matrix, we can remove this decomposition.
  if (Platform.OS === 'android') {
    return MatrixMath.decomposeMatrix(result);
  }
  return result;
}

/**
 * Performs a destructive operation on a transform matrix.
 */
function _multiplyTransform(
  result: Array<number>,
  matrixMathFunction: Function,
  args: Array<number>
): void {
  var matrixToApply = MatrixMath.createIdentityMatrix();
  var argsWithIdentity = [matrixToApply].concat(args);
  matrixMathFunction.apply(this, argsWithIdentity);
  MatrixMath.multiplyInto(result, result, matrixToApply);
}

/**
 * Parses a string like '0.5rad' or '60deg' into radians expressed in a float.
 * Note that validation on the string is done in `_validateTransform()`.
 */
function _convertToRadians(value: string): number {
  var floatValue = parseFloat(value, 10);
  return value.indexOf('rad') > -1 ? floatValue : floatValue * Math.PI / 180;
}

function _validateTransform(key, value, transformation) {
  invariant(
    !value.getValue,
    'You passed an Animated.Value to a normal component. ' +
    'You need to wrap that component in an Animated. For example, ' +
    'replace <View /> by <Animated.View />.'
  );

  var multivalueTransforms = [
    'matrix',
    'translate',
  ];
  if (multivalueTransforms.indexOf(key) !== -1) {
    invariant(
      Array.isArray(value),
      'Transform with key of %s must have an array as the value: %s',
      key,
      stringifySafe(transformation),
    );
  }
  switch (key) {
    case 'matrix':
      invariant(
        value.length === 9 || value.length === 16,
        'Matrix transform must have a length of 9 (2d) or 16 (3d). ' +
          'Provided matrix has a length of %s: %s',
        value.length,
        stringifySafe(transformation),
      );
      break;
    case 'translate':
      break;
    case 'rotateX':
    case 'rotateY':
    case 'rotateZ':
    case 'rotate':
    case 'skewX':
    case 'skewY':
      invariant(
        typeof value === 'string',
        'Transform with key of "%s" must be a string: %s',
        key,
        stringifySafe(transformation),
      );
      invariant(
        value.indexOf('deg') > -1 || value.indexOf('rad') > -1,
        'Rotate transform must be expressed in degrees (deg) or radians ' +
          '(rad): %s',
        stringifySafe(transformation),
      );
      break;
    case 'perspective':
      invariant(
        typeof value === 'number',
        'Transform with key of "%s" must be a number: %s',
        key,
        stringifySafe(transformation),
      );
      invariant(
        value !== 0,
        'Transform with key of "%s" cannot be zero: %s',
        key,
        stringifySafe(transformation),
      );
      break;
    default:
      invariant(
        typeof value === 'number',
        'Transform with key of "%s" must be a number: %s',
        key,
        stringifySafe(transformation),
      );
  }
}

module.exports = processTransform;
<|endoftext|>


<commit_before>angular.module("umbraco.install").factory('installerService', function($rootScope, $q, $timeout, $http, $location, $log){

	var _status = {
		index: 0,
		current: undefined,
		steps: undefined,
		loading: true,
		progress: "100%"
	};

	var factTimer = undefined;
	var _installerModel = {
	    installId: undefined,
        instructions: {
        }
	};

	//add to umbraco installer facts here
	var facts = ['Umbraco helped millions of people watch a man jump from the edge of space',
					'Over 250.000 websites are currently powered by Umbraco',
					'On an average day, more then 1000 people download Umbraco',
					'umbraco.tv is the premier source of Umbraco video tutorials to get you started',
					'You can find the worlds friendliest CMS community at our.umbraco.org',
					'You can become a certified Umbraco developer by attending one of the official courses',
					'Umbraco works really well on tablets',
					'You have 100% control over your markup and design when crafting a website in Umbraco',
					'Umbraco is the best of both worlds: 100% free and open source, and backed by a professional and profitable company',
					"There's a pretty big chance, you've visited a website powered by Umbraco today",
					"'Umbraco-spotting' is the game of spotting big brands running Umbraco",
					"Atleast 2 people have the Umbraco logo tattooed on them",
					"'Umbraco' is the danish name for an allen key",
					"Umbraco has been around since 2005, that's a looong time in IT",
					"More than 400 people from all over the world meet each year in Denmark in June for our annual conference CodeGarden",
					"While you install Umbraco someone else on the other side of the planet probably does it too",
					"You can extend Umbraco without modifying the source code and using either javascript or c#"
					 ];

    /**
        Returns the description for the step at a given index based on the order of the serverOrder of steps
        Since they don't execute on the server in the order that they are displayed in the UI.
    */
	function getDescriptionForStepAtIndex(steps, index) {
	    var sorted = _.sortBy(steps, "serverOrder");
	    if (sorted[index]) {
	        return sorted[index].description;
	    }
	    return null;
	}
    /* Returns the description for the given step name */
	function getDescriptionForStepName(steps, name) {
	    var found = _.find(steps, function(i) {
	        return i.name == name;
	    });
	    return (found) ? found.description : null;
	}

	//calculates the offset of the progressbar on the installaer
	function calculateProgress(steps, next) {
		var pct = "100%";
		var f = _.find(steps, function(item, index) {
			if(item.name == next){
				pct = Math.floor((index+1 / steps.length * 100)) + "%";
				return true;
			}else{
				return false;
			}
		});
	    return  pct;
	}

	//helpful defaults for the view loading
	function resolveView(view){

		if(view.indexOf(".html") < 0){
			view = view + ".html";
		}
		if(view.indexOf("/") < 0){
			view = "views/install/" + view;
		}

		return view;
	}

    /** Have put this here because we are not referencing our other modules */
	function safeApply (scope, fn) {
	    if (scope.$$phase || scope.$root.$$phase) {
	        if (angular.isFunction(fn)) {
	            fn();
	        }
	    }
	    else {
	        if (angular.isFunction(fn)) {
	            scope.$apply(fn);
	        }
	        else {
	            scope.$apply();
	        }
	    }
	}

	var service = {

		status : _status,
		//loads the needed steps and sets the intial state
		init : function(){
			service.status.loading = true;
			if(!_status.all){
				service.getSteps().then(function(response){
					service.status.steps = response.data.steps;
					service.status.index = 0;
					_installerModel.installId = response.data.installId;
					service.findNextStep();

					$timeout(function(){
						service.status.loading = false;
						service.status.configuring = true;
					}, 2000);
				});
			}
		},

		//loads available packages from our.umbraco.org
		getPackages : function(){
			return $http.get(Umbraco.Sys.ServerVariables.installApiBaseUrl + "GetPackages");
		},

		getSteps : function(){
			return $http.get(Umbraco.Sys.ServerVariables.installApiBaseUrl + "GetSetup");
		},

		gotoStep : function(index){
			var step = service.status.steps[index];
			step.view = resolveView(step.view);

			if(!step.model){
				step.model = {};
			}

			service.status.index = index;
			service.status.current = step;
			service.retrieveCurrentStep();
		},

		gotoNamedStep : function(stepName){
			var step = _.find(service.status.steps, function(s, index){
				if (s.view && s.name === stepName) {
					service.status.index = index;
					return true;
				}
				return false;
			});

			step.view = resolveView(step.view);
			if(!step.model){
				step.model = {};
			}
			service.retrieveCurrentStep();
			service.status.current = step;
		},

	    /**
            Finds the next step containing a view. If one is found it stores it as the current step
            and retreives the step information and returns it, otherwise returns null .
        */
		findNextStep : function(){
			var step = _.find(service.status.steps, function(s, index){
				if(s.view && index >= service.status.index){
					service.status.index = index;
					return true;
				}
			    return false;
			});

            if (step) {
                if (step.view.indexOf(".html") < 0) {
                    step.view = step.view + ".html";
                }

                if (step.view.indexOf("/") < 0) {
                    step.view = "views/install/" + step.view;
                }

                if (!step.model) {
                    step.model = {};
                }

                service.status.current = step;
                service.retrieveCurrentStep();

                //returns the next found step
                return step;
            }
            else {
                //there are no more steps found containing a view so return null
                return null;
            }
		},

		storeCurrentStep : function(){
			_installerModel.instructions[service.status.current.name] = service.status.current.model;
		},

		retrieveCurrentStep : function(){
			if(_installerModel.instructions[service.status.current.name]){
				service.status.current.model = _installerModel.instructions[service.status.current.name];
			}
		},

        /** Moves the installer forward to the next view, if there are not more views than the installation will commence */
		forward : function(){
			service.storeCurrentStep();
			service.status.index++;
			var found = service.findNextStep();
            if (!found) {
                //no more steps were found so start the installation process
                service.install();
            }
		},

		backwards : function(){
			service.storeCurrentStep();
			service.gotoStep(service.status.index--);
		},

		install : function(){
			service.storeCurrentStep();
			service.switchToFeedback();

			service.status.feedback = getDescriptionForStepAtIndex(service.status.steps, 0);
			service.status.progress = 0;

			function processInstallStep(){
				$http.post(Umbraco.Sys.ServerVariables.installApiBaseUrl + "PostPerformInstall",
					_installerModel).then(function(response){
						if(!response.data.complete){

							//progress feedback
							service.status.progress = calculateProgress(service.status.steps, response.data.nextStep);

							if(response.data.view){
								//set the current view and model to whatever the process returns, the view is responsible for retriggering install();
								var v = resolveView(response.data.view);
								service.status.current = {view: v, model: response.data.model};

								//turn off loading bar and feedback
								service.switchToConfiguration();
							}
							else {
							    var desc = getDescriptionForStepName(service.status.steps, response.data.nextStep);
								if (desc) {
									service.status.feedback = desc;
								}
								processInstallStep();
							}
						}
						else {
							service.complete();
						}
					}, function(err){
							//this is where we handle installer error
							var v = err.data.view ? resolveView(err.data.view) : resolveView("error");
							var model = err.data.model ? err.data.model : err.data;

							service.status.current = {view: v, model: model};
							service.switchToConfiguration();
					});
			}
			processInstallStep();
		},

		randomFact: function () {
		    safeApply($rootScope, function() {
		        service.status.fact = facts[_.random(facts.length - 1)];
		    });
		},

		switchToFeedback : function(){
			service.status.current = undefined;
			service.status.loading = true;
			service.status.configuring = false;

			//initial fact
			service.randomFact();

			//timed facts
			factTimer = window.setInterval(function(){
				service.randomFact();
			},6000);
		},

		switchToConfiguration : function(){
			service.status.loading = false;
			service.status.configuring = true;
			service.status.feedback = undefined;
			service.status.fact = undefined;

			if(factTimer){
				clearInterval(factTimer);
			}
		},

		complete : function(){

			service.status.progress = "100%";
			service.status.done = true;
			service.status.feedback = "Redirecting you to Umbraco, please wait";
			service.status.loading = false;

			if(factTimer){
				clearInterval(factTimer);
			}

			$timeout(function(){
				window.location.href = Umbraco.Sys.ServerVariables.umbracoBaseUrl;
			}, 1500);
		}
	};

	return service;
});<commit_msg>fix typos, thousands separator and casing of installer facts<commit_after>angular.module("umbraco.install").factory('installerService', function($rootScope, $q, $timeout, $http, $location, $log){

	var _status = {
		index: 0,
		current: undefined,
		steps: undefined,
		loading: true,
		progress: "100%"
	};

	var factTimer = undefined;
	var _installerModel = {
	    installId: undefined,
        instructions: {
        }
	};

	//add to umbraco installer facts here
	var facts = ['Umbraco helped millions of people watch a man jump from the edge of space',
					'Over 250.000 websites are currently powered by Umbraco',
					'On an average day, more than 1.000 people download Umbraco',
					'umbraco.tv is the premier source of Umbraco video tutorials to get you started',
					'You can find the world\'s friendliest CMS community at our.umbraco.org',
					'You can become a certified Umbraco developer by attending one of the official courses',
					'Umbraco works really well on tablets',
					'You have 100% control over your markup and design when crafting a website in Umbraco',
					'Umbraco is the best of both worlds: 100% free and open source, and backed by a professional and profitable company',
					"There's a pretty big chance, you've visited a website powered by Umbraco today",
					"'Umbraco-spotting' is the game of spotting big brands running Umbraco",
					"At least 2 people have the Umbraco logo tattooed on them",
					"'Umbraco' is the danish name for an allen key",
					"Umbraco has been around since 2005, that's a looong time in IT",
					"More than 400 people from all over the world meet each year in Denmark in June for our annual conference CodeGarden",
					"While you install Umbraco someone else on the other side of the planet probably does it too",
					"You can extend Umbraco without modifying the source code and using either JavaScript or C#"
					 ];

    /**
        Returns the description for the step at a given index based on the order of the serverOrder of steps
        Since they don't execute on the server in the order that they are displayed in the UI.
    */
	function getDescriptionForStepAtIndex(steps, index) {
	    var sorted = _.sortBy(steps, "serverOrder");
	    if (sorted[index]) {
	        return sorted[index].description;
	    }
	    return null;
	}
    /* Returns the description for the given step name */
	function getDescriptionForStepName(steps, name) {
	    var found = _.find(steps, function(i) {
	        return i.name == name;
	    });
	    return (found) ? found.description : null;
	}

	//calculates the offset of the progressbar on the installaer
	function calculateProgress(steps, next) {
		var pct = "100%";
		var f = _.find(steps, function(item, index) {
			if(item.name == next){
				pct = Math.floor((index+1 / steps.length * 100)) + "%";
				return true;
			}else{
				return false;
			}
		});
	    return  pct;
	}

	//helpful defaults for the view loading
	function resolveView(view){

		if(view.indexOf(".html") < 0){
			view = view + ".html";
		}
		if(view.indexOf("/") < 0){
			view = "views/install/" + view;
		}

		return view;
	}

    /** Have put this here because we are not referencing our other modules */
	function safeApply (scope, fn) {
	    if (scope.$$phase || scope.$root.$$phase) {
	        if (angular.isFunction(fn)) {
	            fn();
	        }
	    }
	    else {
	        if (angular.isFunction(fn)) {
	            scope.$apply(fn);
	        }
	        else {
	            scope.$apply();
	        }
	    }
	}

	var service = {

		status : _status,
		//loads the needed steps and sets the intial state
		init : function(){
			service.status.loading = true;
			if(!_status.all){
				service.getSteps().then(function(response){
					service.status.steps = response.data.steps;
					service.status.index = 0;
					_installerModel.installId = response.data.installId;
					service.findNextStep();

					$timeout(function(){
						service.status.loading = false;
						service.status.configuring = true;
					}, 2000);
				});
			}
		},

		//loads available packages from our.umbraco.org
		getPackages : function(){
			return $http.get(Umbraco.Sys.ServerVariables.installApiBaseUrl + "GetPackages");
		},

		getSteps : function(){
			return $http.get(Umbraco.Sys.ServerVariables.installApiBaseUrl + "GetSetup");
		},

		gotoStep : function(index){
			var step = service.status.steps[index];
			step.view = resolveView(step.view);

			if(!step.model){
				step.model = {};
			}

			service.status.index = index;
			service.status.current = step;
			service.retrieveCurrentStep();
		},

		gotoNamedStep : function(stepName){
			var step = _.find(service.status.steps, function(s, index){
				if (s.view && s.name === stepName) {
					service.status.index = index;
					return true;
				}
				return false;
			});

			step.view = resolveView(step.view);
			if(!step.model){
				step.model = {};
			}
			service.retrieveCurrentStep();
			service.status.current = step;
		},

	    /**
            Finds the next step containing a view. If one is found it stores it as the current step
            and retreives the step information and returns it, otherwise returns null .
        */
		findNextStep : function(){
			var step = _.find(service.status.steps, function(s, index){
				if(s.view && index >= service.status.index){
					service.status.index = index;
					return true;
				}
			    return false;
			});

            if (step) {
                if (step.view.indexOf(".html") < 0) {
                    step.view = step.view + ".html";
                }

                if (step.view.indexOf("/") < 0) {
                    step.view = "views/install/" + step.view;
                }

                if (!step.model) {
                    step.model = {};
                }

                service.status.current = step;
                service.retrieveCurrentStep();

                //returns the next found step
                return step;
            }
            else {
                //there are no more steps found containing a view so return null
                return null;
            }
		},

		storeCurrentStep : function(){
			_installerModel.instructions[service.status.current.name] = service.status.current.model;
		},

		retrieveCurrentStep : function(){
			if(_installerModel.instructions[service.status.current.name]){
				service.status.current.model = _installerModel.instructions[service.status.current.name];
			}
		},

        /** Moves the installer forward to the next view, if there are not more views than the installation will commence */
		forward : function(){
			service.storeCurrentStep();
			service.status.index++;
			var found = service.findNextStep();
            if (!found) {
                //no more steps were found so start the installation process
                service.install();
            }
		},

		backwards : function(){
			service.storeCurrentStep();
			service.gotoStep(service.status.index--);
		},

		install : function(){
			service.storeCurrentStep();
			service.switchToFeedback();

			service.status.feedback = getDescriptionForStepAtIndex(service.status.steps, 0);
			service.status.progress = 0;

			function processInstallStep(){
				$http.post(Umbraco.Sys.ServerVariables.installApiBaseUrl + "PostPerformInstall",
					_installerModel).then(function(response){
						if(!response.data.complete){

							//progress feedback
							service.status.progress = calculateProgress(service.status.steps, response.data.nextStep);

							if(response.data.view){
								//set the current view and model to whatever the process returns, the view is responsible for retriggering install();
								var v = resolveView(response.data.view);
								service.status.current = {view: v, model: response.data.model};

								//turn off loading bar and feedback
								service.switchToConfiguration();
							}
							else {
							    var desc = getDescriptionForStepName(service.status.steps, response.data.nextStep);
								if (desc) {
									service.status.feedback = desc;
								}
								processInstallStep();
							}
						}
						else {
							service.complete();
						}
					}, function(err){
							//this is where we handle installer error
							var v = err.data.view ? resolveView(err.data.view) : resolveView("error");
							var model = err.data.model ? err.data.model : err.data;

							service.status.current = {view: v, model: model};
							service.switchToConfiguration();
					});
			}
			processInstallStep();
		},

		randomFact: function () {
		    safeApply($rootScope, function() {
		        service.status.fact = facts[_.random(facts.length - 1)];
		    });
		},

		switchToFeedback : function(){
			service.status.current = undefined;
			service.status.loading = true;
			service.status.configuring = false;

			//initial fact
			service.randomFact();

			//timed facts
			factTimer = window.setInterval(function(){
				service.randomFact();
			},6000);
		},

		switchToConfiguration : function(){
			service.status.loading = false;
			service.status.configuring = true;
			service.status.feedback = undefined;
			service.status.fact = undefined;

			if(factTimer){
				clearInterval(factTimer);
			}
		},

		complete : function(){

			service.status.progress = "100%";
			service.status.done = true;
			service.status.feedback = "Redirecting you to Umbraco, please wait";
			service.status.loading = false;

			if(factTimer){
				clearInterval(factTimer);
			}

			$timeout(function(){
				window.location.href = Umbraco.Sys.ServerVariables.umbracoBaseUrl;
			}, 1500);
		}
	};

	return service;
});<|endoftext|>


<commit_before>Ext.define("Starter.model.User",
{
  extend : "Ext.data.Model",
  requires : [ "Ext.data.identifier.Uuid", "Ext.data.proxy.Direct", "Ext.data.validator.Email" ],
  identifier : "uuid",
  fields : [ {
    name : "id",
    type : "string",
    allowNull : true,
    convert : null
  }, {
    name : "lastName",
    type : "string",
    validators : [ {
      type : "notBlank"
    } ]
  }, {
    name : "firstName",
    type : "string",
    validators : [ {
      type : "notBlank"
    } ]
  }, {
    name : "email",
    type : "string",
    validators : [ {
      type : "email"
    } ]
  }, {
    name : "locale",
    type : "string",
    validators : [ {
      type : "notBlank"
    } ]
  }, {
    name : "enabled",
    type : "boolean"
  }, {
    name : "failedLogins",
    type : "integer",
    persist : false
  }, {
    name : "lockedOutUntil",
    type : "date",
    dateFormat : "time",
    persist : false
  }, {
    name : "lastAccess",
    type : "date",
    dateFormat : "time",
    persist : false
  }, {
    name : "twoFactorAuth",
    type : "boolean",
    persist : false
  } ],
  proxy : {
    type : "direct",
    api : {
      read : "userService.read",
      create : "userService.update",
      update : "userService.update",
      destroy : "userService.destroy"
    },
    reader : {
      rootProperty : "records"
    },
    writer : {
      writeAllFields : true
    }
  }
});<commit_msg>Email is a mandatory field<commit_after>Ext.define("Starter.model.User",
{
  extend : "Ext.data.Model",
  requires : [ "Ext.data.identifier.Uuid", "Ext.data.proxy.Direct", "Ext.data.validator.Email" ],
  identifier : "uuid",
  fields : [ {
    name : "id",
    type : "string",
    allowNull : true,
    convert : null
  }, {
    name : "lastName",
    type : "string",
    validators : [ {
      type : "notBlank"
    } ]
  }, {
    name : "firstName",
    type : "string",
    validators : [ {
      type : "notBlank"
    } ]
  }, {
    name : "email",
    type : "string",
    validators : [ {
      type : "email"
    }, {
      type : "notBlank"
    } ]
  }, {
    name : "locale",
    type : "string",
    validators : [ {
      type : "notBlank"
    } ]
  }, {
    name : "enabled",
    type : "boolean"
  }, {
    name : "failedLogins",
    type : "integer",
    persist : false
  }, {
    name : "lockedOutUntil",
    type : "date",
    dateFormat : "time",
    persist : false
  }, {
    name : "lastAccess",
    type : "date",
    dateFormat : "time",
    persist : false
  }, {
    name : "twoFactorAuth",
    type : "boolean",
    persist : false
  } ],
  proxy : {
    type : "direct",
    api : {
      read : "userService.read",
      create : "userService.update",
      update : "userService.update",
      destroy : "userService.destroy"
    },
    reader : {
      rootProperty : "records"
    },
    writer : {
      writeAllFields : true
    }
  }
});<|endoftext|>


<commit_before>currentAction = "none";
templates = {};
var unFocus = function () {
  if (document.selection) {
    document.selection.empty()
  } else {
    window.getSelection().removeAllRanges()
  }
}

Template.post.onCreated(function () {
    templates[this.data._id] = this;
    this.linkCount = new ReactiveVar(0);

    let linksCursor = Link.find({ $or: [ { sourceId: this.data._id}, { targetId: this.data._id} ] });

    let self = this;

    linksCursor.observe({
        added: function(doc) {
            let _id;
            if (self.data._id == doc.sourceId)
                _id = doc.targetId;
            if (self.data._id == doc.targetId)
                _id = doc.sourceId;

            if (!templates[_id])
                self.linkCount.set(self.linkCount.get() + 1);
        },
        removed: function(doc) {
            if (!templates[doc._id])
                self.linkCount.set(self.linkCount.get() - 1);
        }
    });
});

Template.post.onRendered(function () {
    var instance = Template.instance();

    var postLink = Template.instance().$('.titleBar a');
    postLink.attr('title', postLink.text());

    var usernameLink = Template.instance().$('.username');
    usernameLink.attr('title', usernameLink.text());

    /*instance.$('.postContent').dotdotdot({
        after: 'a.readMoreLink'
    });*/

    Link.find({ $or: [ { sourceId: this.data._id}, { targetId: this.data._id} ] })
    .forEach(function(link) {
        tree.addLink(link);
    });

    Link.find({sourceId: this.data._id}).forEach(function(link) {
        handlers.addHandler(link.targetId);
        var temp = templates[link.targetId];
        if (temp) temp.linkCount.set(temp.linkCount.get() - 1);
    });
    Link.find({targetId: this.data._id}).forEach(function(link) {
        handlers.addHandler(link.sourceId);
        var temp = templates[link.sourceId];
        if (temp) temp.linkCount.set(temp.linkCount.get() - 1);
    });
    tree.runGraph();
    tree.render();
});

Template.post.onDestroyed(function () {
    var self = this;
    Link.find({sourceId: this.data._id}).forEach(function(link) {
        var temp = templates[link.targetId];
        if (temp) temp.linkCount.set(temp.linkCount.get() + 1);
    });
    Link.find({targetId: this.data._id}).forEach(function(link) {
        var temp = templates[link.sourceId];
        if (temp) temp.linkCount.set(temp.linkCount.get() + 1);
    });

    delete templates[this.data._id];
});

Template.post.helpers({
    avatarURL: function() {
        return 'https://avatars3.githubusercontent.com/u/6981448';
    },
    replyCount: function() {
        return Template.instance().linkCount.get();
    },
    user: function() {
        return Meteor.users.findOne(this.ownerId);
    },
    hasContent: function() {
        return (this.content && this.content.length > 0);
    }
});

Template.post.events({
    'click': function(event) {
        switch (currentAction) {
            case "deleting":
                event.stopImmediatePropagation();
                if ((this.ownerId === Meteor.userId() ||
                    Roles.userIsInRole(Meteor.userId(), ['moderator'])) &&
                    confirm("Are you sure you want to permanently delete this post?")) {

                    tree.removeNode(this);
                    handlers.stop(this._id);
                    Meteor.call('removeWithLinks', this._id);
                }
                break;

        }

    },
    'mousedown .unDraggable, touchstart .unDraggable': function(event) {
        event.stopImmediatePropagation();
    },
    'mousedown .draggable, touchstart .draggable': function(event) {
        event.stopImmediatePropagation();
        this.dragging = true;
        this.counter = 0;
        this.mousePos = {x: event.screenX, y: event.screenY};
    },
    'mouseup, touchend': function(event) {
        unFocus();
        this.dragging = false;
        tree.render();
    },
    'mousemove, touchmove': function(event) {
        if (this.dragging) {
            unFocus();
            let node = tree.findNode(this);
            node.x += (event.screenX - this.mousePos.x);
            node.y += (event.screenY - this.mousePos.y);
            this.mousePos = {x: event.screenX, y: event.screenY};
        }
        if (this.counter <= 0) {
            tree.render();
            this.counter = 2;
        } else this.counter--;
    },
    'click .showRepliesButton': function (event) {
        Link.find({sourceId: this._id})
        .forEach(function(link) {
            var postToAdd = Post.findOne({_id: link.targetId});
            if (postToAdd && !nodesInGraph.findOne({_id: postToAdd._id})) {
                postToAdd.type = "post";
                handlers.addHandler(postToAdd._id);
                tree.addNode(postToAdd);
            }
        });
        Link.find({targetId: this._id})
        .forEach(function(link) {
            var postToAdd = Post.findOne({_id: link.sourceId});
            if (postToAdd && !nodesInGraph.findOne({_id: postToAdd._id})) {
                postToAdd.type = "post";
                tree.addNode(postToAdd);
                handlers.addHandler(postToAdd._id);
            }
        });
    },
    'click .replyButton': function(event) {
        if (!Meteor.userId()) return;
        if (!nodesInGraph.findOne({type: "reply"})) {
            let _id = tree.addNode({type: "reply", links: [this._id]})._id;
            tree.addLink({sourceId: _id, targetId: this._id});
        } else {
            let reply = nodesInGraph.findOne({type: "reply"});
            let self = this;
            if (!reply.links.find(function(link) {
                return (link == self._id);
            })) {
                nodesInGraph.update({_id: reply._id}, { $push: { links: this._id}});
                tree.addLink({sourceId: reply._id, targetId: this._id});
            } else {
                nodesInGraph.update({_id: reply._id}, { $pull: { links: this._id}});
                tree.removeLink({sourceId: reply._id, targetId: this._id});
            }
        }
    },
    'click .closeButton': function(event) {
        let reply = nodesInGraph.findOne({type: "reply"});
        let self = this;
        if (reply) {
            nodesInGraph.update({_id: reply._id}, { $pull: { links: this._id}});
            tree.removeLink({sourceId: reply._id, targetId: this._id});
        }
        tree.removeNode(this);
    }

});

Template.reply.onRendered(function () {
    var instance = Template.instance();

    tree.runGraph();
    tree.render();
});

Template.reply.events({
    'mousedown .unDraggable, touchstart .unDraggable': function(event) {
        event.stopImmediatePropagation();
    },
    'mousedown draggable, touchstart .draggable': function(event) {
        event.stopImmediatePropagation();
        this.dragging = true;
        this.counter = 0;
        this.mousePos = {x: event.screenX, y: event.screenY};
    },
    'mouseup, touchend': function(event) {
        unFocus();
        this.dragging = false;
        tree.render();
    },
    'mousemove, touchmove': function(event) {
        if (this.dragging) {
            unFocus();
            let node = tree.findNode(this);
            node.x += (event.screenX - this.mousePos.x);
            node.y += (event.screenY - this.mousePos.y);
            this.mousePos = {x: event.screenX, y: event.screenY};
        }
        if (this.counter <= 0) {
            tree.render();
            this.counter = 2;
        } else this.counter--;
    },
    'click .closeButton': function(event) {
        tree.removeNode(this);
    },
    'click .submitButton': function(event) {
        if (!Meteor.userId() || this.links.length < 1) return;
        let title = $('#titleInput-' + this._id).val();
        let content = $('#contentInput-' + this._id).val();
        let newReplyPost = {
            links: this.links,
            title: title,
            content: content

        };
        let postId = Post.insert(newReplyPost);
        handlers.addHandler(postId);
        setTimeout(function() {
            let doc = Post.findOne({_id: postId});
            doc.type = "post";
            tree.addNode(doc);
        }, 1000);
        tree.removeNode(this);
    }
});

Template.forumIndex.onRendered(function () {
    var instance = Template.instance();

    Template.instance().scale = 1;
});

Template.forumIndex.events({
    'mousedown, touchstart': function(event, template) {
        template.dragging = true;
        template.counter = 0;
        template.mousePos = {x: event.screenX, y: event.screenY};
    },
    'mouseup, touchend, mouseleave': function(event, template) {
        template.dragging = false;
        tree.render();
    },
    'mousemove, touchmove': function(event, template) {
        if (template.dragging) {
            unFocus();
            for (let i = 0; i < tree.nodes.length; i++) {
                tree.nodes[i].x += (event.screenX - template.mousePos.x);
                tree.nodes[i].y += (event.screenY - template.mousePos.y);
            }
            template.mousePos = {x: event.screenX, y: event.screenY};

            if (template.counter <= 0) {
                tree.render();
                template.counter = 2;
            } else template.counter--;
        }
    },
    'wheel': function(event) {
        if (event.originalEvent.deltaY < 0) {
            Template.instance().scale *= 4;
            Template.instance().scale /= 3;
        } else {
            Template.instance().scale *= 3;
            Template.instance().scale /= 4;
        }
        $(".graphContainer").css("transform", "scale(" +
        Template.instance().scale + ")");
    },
    'click .button-delete': function(event) {
        event.preventDefault();
        if (currentAction != "deleting") currentAction = "deleting";
        else currentAction = "none";
    },
});

Template.forumIndex.helpers({
    posts() {
        return nodesInGraph.find({type: "post"});
    },
    replies() {
        return nodesInGraph.find({type: "reply"});
    }
});

Template.forumIndex.rendered = function() {
    var init = true;

    var nodesCursor = Post.find({}), linksCursor = Link.find({});

    tree = new ForumTree(this, nodesCursor, linksCursor);

    nodesCursor.observe({
        added: function(doc) {
            if (init) return;
            if (doc.isRoot) {
                doc.type = "post";
                tree.addNode(doc);
            }
        },
        removed: function(doc) {
            if (init) return;
            tree.removeNode(doc);
        }
    });

    linksCursor.observe({
        added: function(doc) {
            if (init) return;
            if (nodesInGraph.findOne({_id: doc.sourceId})) {
                handlers.addHandler(doc.targetId);
            } else if (nodesInGraph.findOne({_id: doc.targetId})) {
                handlers.addHandler(doc.sourceId);
            }
            tree.addLink(doc);
        },
        removed: function(doc) {
            if (init) return;
            tree.removeLink(doc);
        }
    });

    tree.runGraph();
    tree.render();
    init = false;
};
<commit_msg>Fixed bug with innaccurate connecting post counters.<commit_after>currentAction = "none";
templates = {};
var unFocus = function () {
  if (document.selection) {
    document.selection.empty()
  } else {
    window.getSelection().removeAllRanges()
  }
}

Template.post.onCreated(function () {
    templates[this.data._id] = this;
    this.linkCount = new ReactiveVar(0);

    let linksCursor = Link.find({ $or: [ { sourceId: this.data._id}, { targetId: this.data._id} ] });

    let self = this;

    linksCursor.observe({
        added: function(doc) {
            let _id;
            if (self.data._id == doc.sourceId)
                _id = doc.targetId;
            if (self.data._id == doc.targetId)
                _id = doc.sourceId;

            if (!templates[_id])
                self.linkCount.set(self.linkCount.get() + 1);
        },
        removed: function(doc) {
            if (!templates[doc._id])
                self.linkCount.set(self.linkCount.get() - 1);
        }
    });

    Link.find({sourceId: this.data._id}).forEach(function(link) {
        handlers.addHandler(link.targetId);
        var temp = templates[link.targetId];
        if (temp) temp.linkCount.set(temp.linkCount.get() - 1);
    });
    Link.find({targetId: this.data._id}).forEach(function(link) {
        handlers.addHandler(link.sourceId);
        var temp = templates[link.sourceId];
        if (temp) temp.linkCount.set(temp.linkCount.get() - 1);
    });
});

Template.post.onRendered(function () {
    var instance = Template.instance();

    var postLink = Template.instance().$('.titleBar a');
    postLink.attr('title', postLink.text());

    var usernameLink = Template.instance().$('.username');
    usernameLink.attr('title', usernameLink.text());

    /*instance.$('.postContent').dotdotdot({
        after: 'a.readMoreLink'
    });*/

    Link.find({ $or: [ { sourceId: this.data._id}, { targetId: this.data._id} ] })
    .forEach(function(link) {
        tree.addLink(link);
    });

    tree.runGraph();
    tree.render();
});

Template.post.onDestroyed(function () {
    var self = this;
    Link.find({sourceId: this.data._id}).forEach(function(link) {
        var temp = templates[link.targetId];
        if (temp) temp.linkCount.set(temp.linkCount.get() + 1);
    });
    Link.find({targetId: this.data._id}).forEach(function(link) {
        var temp = templates[link.sourceId];
        if (temp) temp.linkCount.set(temp.linkCount.get() + 1);
    });

    delete templates[this.data._id];
});

Template.post.helpers({
    avatarURL: function() {
        return 'https://avatars3.githubusercontent.com/u/6981448';
    },
    replyCount: function() {
        return Template.instance().linkCount.get();
    },
    user: function() {
        return Meteor.users.findOne(this.ownerId);
    },
    hasContent: function() {
        return (this.content && this.content.length > 0);
    }
});

Template.post.events({
    'click': function(event) {
        switch (currentAction) {
            case "deleting":
                event.stopImmediatePropagation();
                if ((this.ownerId === Meteor.userId() ||
                    Roles.userIsInRole(Meteor.userId(), ['moderator'])) &&
                    confirm("Are you sure you want to permanently delete this post?")) {

                    tree.removeNode(this);
                    handlers.stop(this._id);
                    Meteor.call('removeWithLinks', this._id);
                }
                break;

        }

    },
    'mousedown .unDraggable, touchstart .unDraggable': function(event) {
        event.stopImmediatePropagation();
    },
    'mousedown .draggable, touchstart .draggable': function(event) {
        event.stopImmediatePropagation();
        this.dragging = true;
        this.counter = 0;
        this.mousePos = {x: event.screenX, y: event.screenY};
    },
    'mouseup, touchend': function(event) {
        unFocus();
        this.dragging = false;
        tree.render();
    },
    'mousemove, touchmove': function(event) {
        if (this.dragging) {
            unFocus();
            let node = tree.findNode(this);
            node.x += (event.screenX - this.mousePos.x);
            node.y += (event.screenY - this.mousePos.y);
            this.mousePos = {x: event.screenX, y: event.screenY};
        }
        if (this.counter <= 0) {
            tree.render();
            this.counter = 2;
        } else this.counter--;
    },
    'click .showRepliesButton': function (event) {
        Link.find({sourceId: this._id})
        .forEach(function(link) {
            var postToAdd = Post.findOne({_id: link.targetId});
            if (postToAdd && !nodesInGraph.findOne({_id: postToAdd._id})) {
                postToAdd.type = "post";
                handlers.addHandler(postToAdd._id);
                tree.addNode(postToAdd);
            }
        });
        Link.find({targetId: this._id})
        .forEach(function(link) {
            var postToAdd = Post.findOne({_id: link.sourceId});
            if (postToAdd && !nodesInGraph.findOne({_id: postToAdd._id})) {
                postToAdd.type = "post";
                tree.addNode(postToAdd);
                handlers.addHandler(postToAdd._id);
            }
        });
    },
    'click .replyButton': function(event) {
        if (!Meteor.userId()) return;
        if (!nodesInGraph.findOne({type: "reply"})) {
            let _id = tree.addNode({type: "reply", links: [this._id]})._id;
            tree.addLink({sourceId: _id, targetId: this._id});
        } else {
            let reply = nodesInGraph.findOne({type: "reply"});
            let self = this;
            if (!reply.links.find(function(link) {
                return (link == self._id);
            })) {
                nodesInGraph.update({_id: reply._id}, { $push: { links: this._id}});
                tree.addLink({sourceId: reply._id, targetId: this._id});
            } else {
                nodesInGraph.update({_id: reply._id}, { $pull: { links: this._id}});
                tree.removeLink({sourceId: reply._id, targetId: this._id});
            }
        }
    },
    'click .closeButton': function(event) {
        let reply = nodesInGraph.findOne({type: "reply"});
        let self = this;
        if (reply) {
            nodesInGraph.update({_id: reply._id}, { $pull: { links: this._id}});
            tree.removeLink({sourceId: reply._id, targetId: this._id});
        }
        tree.removeNode(this);
    }

});

Template.reply.onRendered(function () {
    var instance = Template.instance();

    tree.runGraph();
    tree.render();
});

Template.reply.events({
    'mousedown .unDraggable, touchstart .unDraggable': function(event) {
        event.stopImmediatePropagation();
    },
    'mousedown draggable, touchstart .draggable': function(event) {
        event.stopImmediatePropagation();
        this.dragging = true;
        this.counter = 0;
        this.mousePos = {x: event.screenX, y: event.screenY};
    },
    'mouseup, touchend': function(event) {
        unFocus();
        this.dragging = false;
        tree.render();
    },
    'mousemove, touchmove': function(event) {
        if (this.dragging) {
            unFocus();
            let node = tree.findNode(this);
            node.x += (event.screenX - this.mousePos.x);
            node.y += (event.screenY - this.mousePos.y);
            this.mousePos = {x: event.screenX, y: event.screenY};
        }
        if (this.counter <= 0) {
            tree.render();
            this.counter = 2;
        } else this.counter--;
    },
    'click .closeButton': function(event) {
        tree.removeNode(this);
    },
    'click .submitButton': function(event) {
        if (!Meteor.userId() || this.links.length < 1) return;
        let title = $('#titleInput-' + this._id).val();
        let content = $('#contentInput-' + this._id).val();
        let newReplyPost = {
            links: this.links,
            title: title,
            content: content

        };
        let postId = Post.insert(newReplyPost);
        handlers.addHandler(postId);
        setTimeout(function() {
            let doc = Post.findOne({_id: postId});
            doc.type = "post";
            tree.addNode(doc);
        }, 1000);
        tree.removeNode(this);
    }
});

Template.forumIndex.onRendered(function () {
    var instance = Template.instance();

    Template.instance().scale = 1;
});

Template.forumIndex.events({
    'mousedown, touchstart': function(event, template) {
        template.dragging = true;
        template.counter = 0;
        template.mousePos = {x: event.screenX, y: event.screenY};
    },
    'mouseup, touchend, mouseleave': function(event, template) {
        template.dragging = false;
        tree.render();
    },
    'mousemove, touchmove': function(event, template) {
        if (template.dragging) {
            unFocus();
            for (let i = 0; i < tree.nodes.length; i++) {
                tree.nodes[i].x += (event.screenX - template.mousePos.x);
                tree.nodes[i].y += (event.screenY - template.mousePos.y);
            }
            template.mousePos = {x: event.screenX, y: event.screenY};

            if (template.counter <= 0) {
                tree.render();
                template.counter = 2;
            } else template.counter--;
        }
    },
    'wheel': function(event) {
        if (event.originalEvent.deltaY < 0) {
            Template.instance().scale *= 4;
            Template.instance().scale /= 3;
        } else {
            Template.instance().scale *= 3;
            Template.instance().scale /= 4;
        }
        $(".graphContainer").css("transform", "scale(" +
        Template.instance().scale + ")");
    },
    'click .button-delete': function(event) {
        event.preventDefault();
        if (currentAction != "deleting") currentAction = "deleting";
        else currentAction = "none";
    },
});

Template.forumIndex.helpers({
    posts() {
        return nodesInGraph.find({type: "post"});
    },
    replies() {
        return nodesInGraph.find({type: "reply"});
    }
});

Template.forumIndex.rendered = function() {
    var init = true;

    var nodesCursor = Post.find({}), linksCursor = Link.find({});

    tree = new ForumTree(this, nodesCursor, linksCursor);

    nodesCursor.observe({
        added: function(doc) {
            if (init) return;
            if (doc.isRoot) {
                doc.type = "post";
                tree.addNode(doc);
            }
        },
        removed: function(doc) {
            if (init) return;
            tree.removeNode(doc);
        }
    });

    linksCursor.observe({
        added: function(doc) {
            if (init) return;
            if (nodesInGraph.findOne({_id: doc.sourceId})) {
                handlers.addHandler(doc.targetId);
            } else if (nodesInGraph.findOne({_id: doc.targetId})) {
                handlers.addHandler(doc.sourceId);
            }
            tree.addLink(doc);
        },
        removed: function(doc) {
            if (init) return;
            tree.removeLink(doc);
        }
    });

    tree.runGraph();
    tree.render();
    init = false;
};
<|endoftext|>


<commit_before>Accounts.ui.config({
    passwordSignupFields : 'USERNAME_ONLY'
});<commit_msg>Add email along with username when user creates an account<commit_after>Accounts.ui.config({
    passwordSignupFields : 'USERNAME_AND_EMAIL'
});<|endoftext|>


<commit_before>var Project = Backbone.Model.extend({
    SRS_DEFAULT: '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs',
    STYLESHEET_DEFAULT: [{
        id: 'style.mss',
        data: '#world {\n  polygon-fill: #eee;\n  line-color: #ccc;\n  line-width: 0.5;\n}'
    }],
    LAYER_DEFAULT: [{
        id: 'world',
        name: 'world',
        srs: '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs',
        Datasource: {
            file: 'http://tilemill-data.s3.amazonaws.com/world_borders_merc.zip',
            type: 'shape',
            estimate_extent: 'true',
            id: 'world'
        }
    }],
    initialize: function(attributes) {
        if (!this.get('srs')) {
            this.set({'srs': this.SRS_DEFAULT});
        }
        if (!this.get('Stylesheet')) {
            this.set({
                'Stylesheet': new StylesheetList(this.STYLESHEET_DEFAULT, {
                    parent: this
                })
            });
        }
        if (!this.get('Layer')) {
            this.set({
                'Layer': new LayerList(this.LAYER_DEFAULT, {
                    parent: this
                })
            });
        }
    },
    parse: function(response) {
        var self = this;
        // Instantiate StylesheetList and LayerList collections from JSON lists
        // of plain JSON objects.
        response.Stylesheet = new StylesheetList(response.Stylesheet ?
                response.Stylesheet :
                [], { parent: this });
        response.Layer = new LayerList(response.Layer ?
                response.Layer : [], { parent: this });
        return response;
    },
    // Override url() method for convenience so we don't always need a
    // collection reference around for CRUD operations on a single model.
    url: function() {
        return '/api/project/' + this.id;
    },
    /**
     * Base64 encode this project's MML URL.
     */
    project64: function(options) {
        // `window.location.origin` is not available in all browsers like
        // Firefox. @TODO This approach won't allow TileMill to be installed in
        // a subdirectory. Fix.
        var url = [window.location.protocol, window.location.host].join('//') + this.url();
        if (options.signed) {
            var md5 = new MD5();
            url += '?' + md5.digest(JSON.stringify(this)).substr(0, 6);
        }
        return Base64.urlsafe_encode(url);
    },
    /**
     * Layer URL based on the model URL.
     */
    layerURL: function(options) {
        return [window.location.protocol, window.location.host].join('//')
            + '/tile/'
            + this.project64(options)
            + '/${z}/${x}/${y}.png';
    },
    validate: function(attributes) {
        if (/^[a-z0-9\-_]+$/i.test(attributes.id) === false) {
            return 'Name must contain only letters, numbers, dashes, and underscores.';
        }
    }
});

var ProjectList = Backbone.Collection.extend({
    model: Project,
    url: '/api/project',
    comparator: function(project) {
        return project.get('id');
    }
});

var ProjectListView = Backbone.View.extend({
    id: 'ProjectListView',
    initialize: function() {
        _.bindAll(this, 'render', 'add', 'about');
        this.collection.bind('add', this.render);
        this.collection.bind('remove', this.render);
        this.collection.fetch({
            success: this.render,
            error: this.render
        });
    },
    render: function() {
        // Render the projects wrapper if not present.
        if ($(this.el).has('ul.projects').length === 0) {
            $(this.el).html(ich.ProjectListView());
            window.app.el.html(this.el);
        }

        // Add a row view for each project. Note that we use a pointer as the
        // projects are added to ensure that when new projects are added on a
        // re-render they are placed at the correct index in the list.
        var that = this;
        var pointer = null;
        this.collection.each(function(project) {
            if (!project.view) {
                project.view = new ProjectRowView({
                    model: project,
                    collection: this.collection
                });
                if (!pointer) {
                    $('ul.projects', self.el).prepend(project.view.el);
                }
                else {
                    $(pointer).after(project.view.el);
                }
            }
            pointer = project.view.el;
        });
        return this;
    },
    events: {
        'click input.submit': 'add',
        'click div#header a.info': 'about'
    },
    add: function() {
        window.app.loading();
        var that = this;
        var project = new Project;
        var success = project.set(
            { id: $('input.text', this.el).val() },
            { error: this.showError }
        );
        if (success) {
            project.save(project, {
                success: function() {
                    $('input.text', this.el).val('');
                    that.collection.add(project);
                    window.app.done();
                },
                error: this.showError
            });
        }
        return false;
    },
    about: function() {
        window.app.message('About TileLive', '@TODO: Put something facinating here.');
        return false;
    },
    showError: function(model, error) {
        window.app.done();
        window.app.message('Error', error);
    }
});

var ProjectRowView = Backbone.View.extend({
    tagName: 'li',
    className: 'clearfix',
    initialize: function() {
        _.bindAll(this, 'render', 'del');
        this.render();
    },
    // See http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#lon.2Flat_to_tile_numbers_2
    thumb: function() {
        var center = this.model.get('center') || {lat: 0, lon: 0, zoom: 2};
        var z = center.zoom;
        var lat_rad = center.lat * Math.PI / 180;
        var x = parseInt((center.lon + 180.0) / 360.0 * Math.pow(2,z));
        var y = parseInt((1.0 - Math.log(Math.tan(lat_rad) + (1 / Math.cos(lat_rad))) / Math.PI) / 2.0 * Math.pow(2,z));
        var url = this.model.layerURL({ signed: true });
        return url.replace('${z}', z).replace('${x}', x).replace('${y}', y);
    },
    render: function() {
        $(this.el).html(ich.ProjectRowView({
            id: this.model.get('id'),
            thumb: this.thumb()
        }));
        return this;
    },
    events: {
        'click .delete': 'del'
    },
    del: function() {
        var that = this;
        window.app.loading();
        if (confirm('Are you sure you want to delete this project?')) {
            this.model.destroy({
                success: function() {
                    that.remove();
                    window.app.done();
                },
                error: function() {
                    window.app.done();
                    window.app.message('Error', 'The project could not be deleted.');
                }
            });
        }
        else {
            window.app.done();
        }
        return false;
    }
});

var ProjectView = Backbone.View.extend({
    id: 'ProjectView',
    events: {
        'click #header a.save': 'saveProject',
        'click #header a.info': 'projectInfo',
        'click #header a.minimal': 'minimal',
        'click #header a.home': 'home'
    },
    initialize: function() {
        _.bindAll(this, 'render', 'saveProject', 'projectInfo',
            'home', 'minimal', 'changed');
        this.model.view = this;
        this.model.bind('change', this.changed);
        this.model.fetch({
            success: this.render,
            error: this.render
        });
    },
    render: function() {
        $(this.el).html(ich.ProjectView(this.model));

        var layers = new LayerListView({
            collection: this.model.get('Layer'),
            project: this.model
        }),
            stylesheets = new StylesheetListView({
            collection: this.model.get('Stylesheet'),
            project: this.model
        }),
            colors = new ColorSwatchesToolView({
            collection: new ColorSwatchesList(null, {
                project: this.model
            }),
            project: this.model
        }),
            map = new MapView({
            model: this.model
        }),
            colorPicker = new ColorPickerToolView({
            model: this.model
        }),
            fontPicker = new FontPickerToolView({
            model: new Abilities,
            project: this.model
        });

        $('#sidebar', this.el).append(layers.el);
        $('#sidebar', this.el).append(colors.el);
        $('#sidebar', this.el).append(map.el);
        $('#toolbar', this.el).append(colorPicker.el);
        $('#toolbar', this.el).append(fontPicker.el);
        $('#main', this.el).append(stylesheets.el);

        window.app.el.html(this.el);
        window.app.trigger('ready');
        return this;
    },
    saveProject: function() {
        var that = this;
        this.model.save(this.model, {
            success: function() {
                that.model.trigger('save');
                $('#header a.save', self.el).removeClass('changed');
                $('.CodeMirror-line-numbers div')
                    .removeClass('syntax-error')
                    .attr('title', '')
                    .unbind('mouseenter mouseleave'); // Removes tipsy.
            },
            error: function(err, data) {
                if (data.status == 500) {
                    var err_obj = $.parseJSON(data.responseText);
                    if (err_obj.line) {
                        var editor = _.detect(
                            that.model.view.stylesheets.collection.models,
                            function(s) {
                                return s.id == err_obj.filename;
                        });
                        $('div:nth-child(' + err_obj.line + ')',
                            editor.view.codemirror.lineNumbers)
                            .addClass('syntax-error')
                            .attr('title', err_obj.message)
                            .tipsy({gravity: 'w'});
                    } else {
                        window.app.message('Error', err_obj.message);
                    }
                }
            }
        });
        return false;
    },
    projectInfo: function() {
        window.app.message('Project Info', {
            'tilelive_url': this.model.layerURL({signed: true}),
            'mml_url': [window.location.protocol, window.location.host].join('//') + this.model.url()
        }, 'projectInfo');
        return false;
    },
    home: function() {
        return (!$('#header a.save', this.el).is('.changed') || confirm('You have unsaved changes. Are you sure you want to close this project?'));
    },
    minimal: function() {
        $('a.minimal', this.el).toggleClass('active');
        if ($('a.minimal', this.el).is('.active')) {
            $(this.el).addClass('minimal');
            this.watcher = new Watcher(this.model);
        }
        else {
            $(this.el).removeClass('minimal');
            this.watcher.destroy();
        }
        return false;
    },
    changed: function() {
        $('#header a.save', this.el).addClass('changed');
    }
});

/**
 * Watcher.
 * Class for updating project in minimal mode.
 */
var Watcher = function(model) {
    _.bindAll(this, 'fetch', 'destroy');
    var model = model;
    this.model = model;
    this.model.bind('change', this.fetch);
    this.md5 = new MD5();
    this.current = this.md5.digest(JSON.stringify(this.model));
    this.watcher = setInterval(function() { model.fetch(); }, 1000);
};

Watcher.prototype.fetch = function() {
    var state = this.md5.digest(JSON.stringify(this.model));
    if (this.current !== state) {
        this.current = state;
        this.model.trigger('save');
    }
};

Watcher.prototype.destroy = function() {
    window.clearInterval(this.watcher);
};

<commit_msg>Fix syntax errors on line 1<commit_after>var Project = Backbone.Model.extend({
    SRS_DEFAULT: '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs',
    STYLESHEET_DEFAULT: [{
        id: 'style.mss',
        data: '#world {\n  polygon-fill: #eee;\n  line-color: #ccc;\n  line-width: 0.5;\n}'
    }],
    LAYER_DEFAULT: [{
        id: 'world',
        name: 'world',
        srs: '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs',
        Datasource: {
            file: 'http://tilemill-data.s3.amazonaws.com/world_borders_merc.zip',
            type: 'shape',
            estimate_extent: 'true',
            id: 'world'
        }
    }],
    initialize: function(attributes) {
        if (!this.get('srs')) {
            this.set({'srs': this.SRS_DEFAULT});
        }
        if (!this.get('Stylesheet')) {
            this.set({
                'Stylesheet': new StylesheetList(this.STYLESHEET_DEFAULT, {
                    parent: this
                })
            });
        }
        if (!this.get('Layer')) {
            this.set({
                'Layer': new LayerList(this.LAYER_DEFAULT, {
                    parent: this
                })
            });
        }
    },
    parse: function(response) {
        var self = this;
        // Instantiate StylesheetList and LayerList collections from JSON lists
        // of plain JSON objects.
        response.Stylesheet = new StylesheetList(response.Stylesheet ?
                response.Stylesheet :
                [], { parent: this });
        response.Layer = new LayerList(response.Layer ?
                response.Layer : [], { parent: this });
        return response;
    },
    // Override url() method for convenience so we don't always need a
    // collection reference around for CRUD operations on a single model.
    url: function() {
        return '/api/project/' + this.id;
    },
    /**
     * Base64 encode this project's MML URL.
     */
    project64: function(options) {
        // `window.location.origin` is not available in all browsers like
        // Firefox. @TODO This approach won't allow TileMill to be installed in
        // a subdirectory. Fix.
        var url = [window.location.protocol, window.location.host].join('//') + this.url();
        if (options.signed) {
            var md5 = new MD5();
            url += '?' + md5.digest(JSON.stringify(this)).substr(0, 6);
        }
        return Base64.urlsafe_encode(url);
    },
    /**
     * Layer URL based on the model URL.
     */
    layerURL: function(options) {
        return [window.location.protocol, window.location.host].join('//')
            + '/tile/'
            + this.project64(options)
            + '/${z}/${x}/${y}.png';
    },
    validate: function(attributes) {
        if (/^[a-z0-9\-_]+$/i.test(attributes.id) === false) {
            return 'Name must contain only letters, numbers, dashes, and underscores.';
        }
    }
});

var ProjectList = Backbone.Collection.extend({
    model: Project,
    url: '/api/project',
    comparator: function(project) {
        return project.get('id');
    }
});

var ProjectListView = Backbone.View.extend({
    id: 'ProjectListView',
    initialize: function() {
        _.bindAll(this, 'render', 'add', 'about');
        this.collection.bind('add', this.render);
        this.collection.bind('remove', this.render);
        this.collection.fetch({
            success: this.render,
            error: this.render
        });
    },
    render: function() {
        // Render the projects wrapper if not present.
        if ($(this.el).has('ul.projects').length === 0) {
            $(this.el).html(ich.ProjectListView());
            window.app.el.html(this.el);
        }

        // Add a row view for each project. Note that we use a pointer as the
        // projects are added to ensure that when new projects are added on a
        // re-render they are placed at the correct index in the list.
        var that = this;
        var pointer = null;
        this.collection.each(function(project) {
            if (!project.view) {
                project.view = new ProjectRowView({
                    model: project,
                    collection: this.collection
                });
                if (!pointer) {
                    $('ul.projects', self.el).prepend(project.view.el);
                }
                else {
                    $(pointer).after(project.view.el);
                }
            }
            pointer = project.view.el;
        });
        return this;
    },
    events: {
        'click input.submit': 'add',
        'click div#header a.info': 'about'
    },
    add: function() {
        window.app.loading();
        var that = this;
        var project = new Project;
        var success = project.set(
            { id: $('input.text', this.el).val() },
            { error: this.showError }
        );
        if (success) {
            project.save(project, {
                success: function() {
                    $('input.text', this.el).val('');
                    that.collection.add(project);
                    window.app.done();
                },
                error: this.showError
            });
        }
        return false;
    },
    about: function() {
        window.app.message('About TileLive', '@TODO: Put something facinating here.');
        return false;
    },
    showError: function(model, error) {
        window.app.done();
        window.app.message('Error', error);
    }
});

var ProjectRowView = Backbone.View.extend({
    tagName: 'li',
    className: 'clearfix',
    initialize: function() {
        _.bindAll(this, 'render', 'del');
        this.render();
    },
    // See http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#lon.2Flat_to_tile_numbers_2
    thumb: function() {
        var center = this.model.get('center') || {lat: 0, lon: 0, zoom: 2};
        var z = center.zoom;
        var lat_rad = center.lat * Math.PI / 180;
        var x = parseInt((center.lon + 180.0) / 360.0 * Math.pow(2,z));
        var y = parseInt((1.0 - Math.log(Math.tan(lat_rad) + (1 / Math.cos(lat_rad))) / Math.PI) / 2.0 * Math.pow(2,z));
        var url = this.model.layerURL({ signed: true });
        return url.replace('${z}', z).replace('${x}', x).replace('${y}', y);
    },
    render: function() {
        $(this.el).html(ich.ProjectRowView({
            id: this.model.get('id'),
            thumb: this.thumb()
        }));
        return this;
    },
    events: {
        'click .delete': 'del'
    },
    del: function() {
        var that = this;
        window.app.loading();
        if (confirm('Are you sure you want to delete this project?')) {
            this.model.destroy({
                success: function() {
                    that.remove();
                    window.app.done();
                },
                error: function() {
                    window.app.done();
                    window.app.message('Error', 'The project could not be deleted.');
                }
            });
        }
        else {
            window.app.done();
        }
        return false;
    }
});

var ProjectView = Backbone.View.extend({
    id: 'ProjectView',
    events: {
        'click #header a.save': 'saveProject',
        'click #header a.info': 'projectInfo',
        'click #header a.minimal': 'minimal',
        'click #header a.home': 'home'
    },
    initialize: function() {
        _.bindAll(this, 'render', 'saveProject', 'projectInfo',
            'home', 'minimal', 'changed');
        this.model.view = this;
        this.model.bind('change', this.changed);
        this.model.fetch({
            success: this.render,
            error: this.render
        });
    },
    render: function() {
        $(this.el).html(ich.ProjectView(this.model));

        var layers = new LayerListView({
            collection: this.model.get('Layer'),
            project: this.model
        }),
            stylesheets = new StylesheetListView({
            collection: this.model.get('Stylesheet'),
            project: this.model
        }),
            colors = new ColorSwatchesToolView({
            collection: new ColorSwatchesList(null, {
                project: this.model
            }),
            project: this.model
        }),
            map = new MapView({
            model: this.model
        }),
            colorPicker = new ColorPickerToolView({
            model: this.model
        }),
            fontPicker = new FontPickerToolView({
            model: new Abilities,
            project: this.model
        });

        $('#sidebar', this.el).append(layers.el);
        $('#sidebar', this.el).append(colors.el);
        $('#sidebar', this.el).append(map.el);
        $('#toolbar', this.el).append(colorPicker.el);
        $('#toolbar', this.el).append(fontPicker.el);
        $('#main', this.el).append(stylesheets.el);

        window.app.el.html(this.el);
        window.app.trigger('ready');
        return this;
    },
    saveProject: function() {
        var that = this;
        this.model.save(this.model, {
            success: function() {
                that.model.trigger('save');
                $('#header a.save', self.el).removeClass('changed');
                $('.CodeMirror-line-numbers div')
                    .removeClass('syntax-error')
                    .attr('title', '')
                    .unbind('mouseenter mouseleave'); // Removes tipsy.
            },
            error: function(err, data) {
                if (data.status == 500) {
                    var err_obj = $.parseJSON(data.responseText);
                    if (err_obj.line) {
                        var editor = _.detect(
                            that.model.view.stylesheets.collection.models,
                            function(s) {
                                return s.id == err_obj.filename;
                        });
                        $('div.CodeMirror-line-numbers div:nth-child(' + err_obj.line + ')',
                            editor.view.codemirror.lineNumbers)
                            .addClass('syntax-error')
                            .attr('title', err_obj.message)
                            .tipsy({gravity: 'w'});
                    } else {
                        window.app.message('Error', err_obj.message);
                    }
                }
            }
        });
        return false;
    },
    projectInfo: function() {
        window.app.message('Project Info', {
            'tilelive_url': this.model.layerURL({signed: true}),
            'mml_url': [window.location.protocol, window.location.host].join('//') + this.model.url()
        }, 'projectInfo');
        return false;
    },
    home: function() {
        return (!$('#header a.save', this.el).is('.changed') || confirm('You have unsaved changes. Are you sure you want to close this project?'));
    },
    minimal: function() {
        $('a.minimal', this.el).toggleClass('active');
        if ($('a.minimal', this.el).is('.active')) {
            $(this.el).addClass('minimal');
            this.watcher = new Watcher(this.model);
        }
        else {
            $(this.el).removeClass('minimal');
            this.watcher.destroy();
        }
        return false;
    },
    changed: function() {
        $('#header a.save', this.el).addClass('changed');
    }
});

/**
 * Watcher.
 * Class for updating project in minimal mode.
 */
var Watcher = function(model) {
    _.bindAll(this, 'fetch', 'destroy');
    var model = model;
    this.model = model;
    this.model.bind('change', this.fetch);
    this.md5 = new MD5();
    this.current = this.md5.digest(JSON.stringify(this.model));
    this.watcher = setInterval(function() { model.fetch(); }, 1000);
};

Watcher.prototype.fetch = function() {
    var state = this.md5.digest(JSON.stringify(this.model));
    if (this.current !== state) {
        this.current = state;
        this.model.trigger('save');
    }
};

Watcher.prototype.destroy = function() {
    window.clearInterval(this.watcher);
};

<|endoftext|>


<commit_before>document.addEventListener("DOMContentLoaded", function () {
  	var waitingInterval;
  	var sockjs_url = 'http://2048.stolarsky.com:3000/game/sockets', sockjs, multiplexer;

  	// Wait till the browser is ready to render the game (avoids glitches)
 	window.requestAnimationFrame(function () {
	 	var startNewGame = function () {
	 			$('#game-start-btn').on('click', function () {
					$('#player-msg').html('Searching for competitor \n.\n.\n.');
					//$('#player-msg').toggle('text-center');
					var fadedOut = false;
					waitingInterval = setInterval(function () {
					  if (fadedOut) {
					    $('.ellipsis:eq(0)').fadeIn(500);
					    setTimeout(function() {
					      $('.ellipsis:eq(1)').fadeIn(500);
					      setTimeout(function() {
					        $('.ellipsis:eq(2)').fadeIn(500);
					      }, 250);
					    }, 250);
					    fadedOut = false;
					  }
					  else {
					    $('.ellipsis:eq(2)').fadeOut(500);
					    setTimeout(function() {
					      $('.ellipsis:eq(1)').fadeOut(500);
					      setTimeout(function() {
					        $('.ellipsis:eq(0)').fadeOut(500);
					      }, 250);
					    }, 250);
					    fadedOut = true;
					  }
					}, 1500);
					$.get('http://2048.stolarsky.com:3000/game/new', startGame);
			  	});
	 	};

		var startGame = function (data) {
		  	data = JSON.parse(data);
		  	var player = data.player;
		  	sockjs = new SockJS(sockjs_url);
		  	multiplexer = new WebSocketMultiplex(sockjs);
			io = multiplexer.channel(data.channel);

			window._io = {
				listeners: [],
				oneTimeListeners: [],
				addListener: function (cb) {
					window._io.listeners.push(cb);
				},
				addOneTimeListener: function (callback, onlyWhen) {
					window._io.oneTimeListeners.push({
						cb: callback,
						condition: onlyWhen
					});
				},
				clearListeners: function () {
					window._io.listeners = [];
					window._io.oneTimeListeners = [];
				}
			}

			io.onopen = function() {
				// console.log('sockjs: open');
				if (data.player === 2)
					io.send(JSON.stringify({player: data.player, start: Date.now() + 5500}));
			};

			io.onmessage = function(event) {
			    var msg = JSON.parse(event.data);
			    //console.log('message:', msg);
			    for (var i = 0, len = window._io.listeners.length; i < len; i++) {
			    	window._io.listeners[i](msg);
			    }
			    for (var i = window._io.oneTimeListeners.length - 1; i >= 0; i--) {
			    	var tempObj = window._io.oneTimeListeners[i];
			    	if (!!tempObj.condition(msg)) {
			    		tempObj.cb(msg);
			    		window._io.oneTimeListeners.splice(i, 1);
			    	}
			    }
			};

			/* Socket Listeners! */
			window._io.addListener(function (msg) {
			    if (msg.player === 0 && msg.size && msg.startCells) {
			    	window._gameBoard = {};
			    	window._gameBoard.size = msg.size;
			    	window._gameBoard.startTiles = msg.startCells;

			    }
			});

			window._io.addOneTimeListener(function (msg) {
			    //$('#player-msg').toggle('text-center');
			    clearInterval(waitingInterval);
			    $('#player-msg').html('Opponent Found!');
			    setTimeout(function () {
			    	window._io.player = {};
			    	window._io.player['1'] = 0;
			    	window._io.player['2'] = 0;
			    	window._io.gameOver = false;
				   	var opposingPlayer = data.player === 1 ? 2 : 1;
				    //var timeLeft = msg.start - Date.now();
				    var times = 3;
				    var countdown = setInterval(function() {
						// Countdown messages
				   		$('#player-msg').html('
Game Will start in ' + times + '
');
				   		times--;
				   		if (times === -1) {
				   			clearInterval(countdown);
				   			$('#player-msg').html('
 BEGIN!
');
				   			var localManager = new GameManager({size: window._gameBoard.size, startTiles: window._gameBoard.startTiles, player: data.player, otherPlayer: opposingPlayer, online: false}, KeyboardInputManager, HTMLActuator, io),
				    			onlineManager = new GameManager({size: window._gameBoard.size, startTiles: window._gameBoard.startTiles, player: opposingPlayer, otherPlayer: data.player, online: true}, OnlineInputManager, HTMLActuator, io);

				    		var gameOver = function (timer, message, connectionIssue) {
								message = message || 'Game over!';
								clearInterval(timer);
								$('#player-msg').html('
' + message + '
');
								window._io.gameOver = true;
								if (connectionIssue) {
									localManager.actuate();
									onlineManager.actuate();
								}
								setTimeout(function () {
									$('#player-msg').fadeOut();
								}, 1000);
								setTimeout(function () {
									$('#player-msg').html('');
									$('#player-msg').fadeIn();
								}, 1500);
								setTimeout(function () {
									$('#player-msg').html('Play Again!');
									startNewGame();
									window._io.clearListeners();
									sockjs.close();
									$('#game-start-btn').on('click', function () {
										localManager.restart();
										onlineManager.restart();
									});
								}, 3000);
							};

				   			var gameTimeLeft = 120;//game timer
				   			var timer = setInterval(function () {
								var sec;
	    						if (gameTimeLeft % 60 === 0)
	    							sec = '00';
	    						else if (('' + gameTimeLeft % 60).length === 1)
	    							sec = '0' + gameTimeLeft % 60;
	    						else
	    						 	sec = gameTimeLeft % 60;
	      						var min = Math.floor(gameTimeLeft/60);
				   				$('#player-msg').html('
' + min + ':' + sec + '
');
				   				gameTimeLeft--;
				   				if (gameTimeLeft === -1) {
				   					gameOver(timer);
				   				}
				   			}, 750);

				   			window._io.addOneTimeListener(function (msg) {
							  	//gameOver(timer);
							}, function (msg) {
								return !!msg.gameEnd;
							});

							window._io.addOneTimeListener(function (msg) {
							  	gameOver(timer, 'Connection Lost :(');
							}, function (msg) {
								return !!msg.dead;
							});
				   		}
				   	}, 1000);
			    }, 1000);
			}, function (msg) {
				return !!msg.start;
			});

			io.onclose = function() {
			    console.log('sockjs: close');
			};
		};

		startNewGame();
  });
});
<commit_msg>Fixed silly mistake - commented out func call<commit_after>document.addEventListener("DOMContentLoaded", function () {
  	var waitingInterval;
  	var sockjs_url = 'http://2048.stolarsky.com:3000/game/sockets', sockjs, multiplexer;

  	// Wait till the browser is ready to render the game (avoids glitches)
 	window.requestAnimationFrame(function () {
	 	var startNewGame = function () {
	 			$('#game-start-btn').on('click', function () {
					$('#player-msg').html('Searching for competitor \n.\n.\n.');
					//$('#player-msg').toggle('text-center');
					var fadedOut = false;
					waitingInterval = setInterval(function () {
					  if (fadedOut) {
					    $('.ellipsis:eq(0)').fadeIn(500);
					    setTimeout(function() {
					      $('.ellipsis:eq(1)').fadeIn(500);
					      setTimeout(function() {
					        $('.ellipsis:eq(2)').fadeIn(500);
					      }, 250);
					    }, 250);
					    fadedOut = false;
					  }
					  else {
					    $('.ellipsis:eq(2)').fadeOut(500);
					    setTimeout(function() {
					      $('.ellipsis:eq(1)').fadeOut(500);
					      setTimeout(function() {
					        $('.ellipsis:eq(0)').fadeOut(500);
					      }, 250);
					    }, 250);
					    fadedOut = true;
					  }
					}, 1500);
					$.get('http://2048.stolarsky.com:3000/game/new', startGame);
			  	});
	 	};

		var startGame = function (data) {
		  	data = JSON.parse(data);
		  	var player = data.player;
		  	sockjs = new SockJS(sockjs_url);
		  	multiplexer = new WebSocketMultiplex(sockjs);
			io = multiplexer.channel(data.channel);

			window._io = {
				listeners: [],
				oneTimeListeners: [],
				addListener: function (cb) {
					window._io.listeners.push(cb);
				},
				addOneTimeListener: function (callback, onlyWhen) {
					window._io.oneTimeListeners.push({
						cb: callback,
						condition: onlyWhen
					});
				},
				clearListeners: function () {
					window._io.listeners = [];
					window._io.oneTimeListeners = [];
				}
			}

			io.onopen = function() {
				// console.log('sockjs: open');
				if (data.player === 2)
					io.send(JSON.stringify({player: data.player, start: Date.now() + 5500}));
			};

			io.onmessage = function(event) {
			    var msg = JSON.parse(event.data);
			    //console.log('message:', msg);
			    for (var i = 0, len = window._io.listeners.length; i < len; i++) {
			    	window._io.listeners[i](msg);
			    }
			    for (var i = window._io.oneTimeListeners.length - 1; i >= 0; i--) {
			    	var tempObj = window._io.oneTimeListeners[i];
			    	if (!!tempObj.condition(msg)) {
			    		tempObj.cb(msg);
			    		window._io.oneTimeListeners.splice(i, 1);
			    	}
			    }
			};

			/* Socket Listeners! */
			window._io.addListener(function (msg) {
			    if (msg.player === 0 && msg.size && msg.startCells) {
			    	window._gameBoard = {};
			    	window._gameBoard.size = msg.size;
			    	window._gameBoard.startTiles = msg.startCells;

			    }
			});

			window._io.addOneTimeListener(function (msg) {
			    //$('#player-msg').toggle('text-center');
			    clearInterval(waitingInterval);
			    $('#player-msg').html('Opponent Found!');
			    setTimeout(function () {
			    	window._io.player = {};
			    	window._io.player['1'] = 0;
			    	window._io.player['2'] = 0;
			    	window._io.gameOver = false;
				   	var opposingPlayer = data.player === 1 ? 2 : 1;
				    //var timeLeft = msg.start - Date.now();
				    var times = 3;
				    var countdown = setInterval(function() {
						// Countdown messages
				   		$('#player-msg').html('
Game Will start in ' + times + '
');
				   		times--;
				   		if (times === -1) {
				   			clearInterval(countdown);
				   			$('#player-msg').html('
 BEGIN!
');
				   			var localManager = new GameManager({size: window._gameBoard.size, startTiles: window._gameBoard.startTiles, player: data.player, otherPlayer: opposingPlayer, online: false}, KeyboardInputManager, HTMLActuator, io),
				    			onlineManager = new GameManager({size: window._gameBoard.size, startTiles: window._gameBoard.startTiles, player: opposingPlayer, otherPlayer: data.player, online: true}, OnlineInputManager, HTMLActuator, io);

				    		var gameOver = function (timer, message, connectionIssue) {
								message = message || 'Game over!';
								clearInterval(timer);
								$('#player-msg').html('
' + message + '
');
								window._io.gameOver = true;
								if (connectionIssue) {
									localManager.actuate();
									onlineManager.actuate();
								}
								setTimeout(function () {
									$('#player-msg').fadeOut();
								}, 1000);
								setTimeout(function () {
									$('#player-msg').html('');
									$('#player-msg').fadeIn();
								}, 1500);
								setTimeout(function () {
									$('#player-msg').html('Play Again!');
									startNewGame();
									window._io.clearListeners();
									sockjs.close();
									$('#game-start-btn').on('click', function () {
										localManager.restart();
										onlineManager.restart();
									});
								}, 3000);
							};

				   			var gameTimeLeft = 120;//game timer
				   			var timer = setInterval(function () {
								var sec;
	    						if (gameTimeLeft % 60 === 0)
	    							sec = '00';
	    						else if (('' + gameTimeLeft % 60).length === 1)
	    							sec = '0' + gameTimeLeft % 60;
	    						else
	    						 	sec = gameTimeLeft % 60;
	      						var min = Math.floor(gameTimeLeft/60);
				   				$('#player-msg').html('
' + min + ':' + sec + '
');
				   				gameTimeLeft--;
				   				if (gameTimeLeft === -1) {
				   					gameOver(timer);
				   				}
				   			}, 750);

				   			window._io.addOneTimeListener(function (msg) {
							  	gameOver(timer);
							}, function (msg) {
								return !!msg.gameEnd;
							});

							window._io.addOneTimeListener(function (msg) {
							  	gameOver(timer, 'Connection Lost :(');
							}, function (msg) {
								return !!msg.dead;
							});
				   		}
				   	}, 1000);
			    }, 1000);
			}, function (msg) {
				return !!msg.start;
			});

			io.onclose = function() {
			    console.log('sockjs: close');
			};
		};

		startNewGame();
  });
});
<|endoftext|>