text: /*! umbraco
   * https://github.com/umbraco/umbraco-cms/
   * Copyright (c) 2016 Umbraco HQ;
   * Licensed 
   */
  
  (function() { 
  
   angular.module('umbraco.install', []); 
  angular.module("umbraco.install").controller("Umbraco.InstallerController",
  	function ($scope, installerService) {
  
  	    //TODO: Decouple the service from the controller - the controller should be responsible
  	    // for the model (state) and the service should be responsible for helping the controller,
  	    // the controller should be passing the model into it's methods for manipulation and not hold
  	    // state. We should not be assigning properties from a service to a controller's scope.
  	    // see: https://github.com/umbraco/Umbraco-CMS/commit/b86ef0d7ac83f699aee35d807f7f7ebb6dd0ed2c#commitcomment-5721204
  
  	    $scope.stepIndex = 0;
  	    //comment this out if you just want to see tips
  	    installerService.init();
  
  	    //uncomment this to see tips
  	    //installerService.switchToFeedback();
  
  	    $scope.installer = installerService.status;
  
  	    $scope.forward = function () {
  	        installerService.forward();
  	    };
  
  	    $scope.backward = function () {
  	        installerService.backward();
  	    };
  
  	    $scope.install = function () {
  	        installerService.install();
  	    };
  
  	    $scope.gotoStep = function (step) {
  	        installerService.gotoNamedStep(step);
  	    };
  
  	    $scope.restart = function () {
  	        installerService.gotoStep(0);
  	    };
  	});
  
  //this ensure that we start with a clean slate on every install and upgrade
  angular.module("umbraco.install").run(function ($templateCache) {
      $templateCache.removeAll();
  });
  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 370 000 websites are currently powered by Umbraco',
  					"At least 2 people have named their cat 'Umbraco'",
  					'On an average day, more than 1000 people download Umbraco',
  					'<a target="_blank" href="http://umbraco.tv">umbraco.tv</a> is the premier source of Umbraco video tutorials to get you started',
  					'You can find the world\'s friendliest CMS community at <a target="_blank" href="http://our.umbraco.org">our.umbraco.org</a>',
  					'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 4 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 <a target='_blank' href='http://codegarden15.com'>CodeGarden</a>", 
  					"While you are installing Umbraco someone else on the other side of the planet is probably doing it too",
  					"You can extend Umbraco without modifying the source code using either JavaScript or C#",
  					"Umbraco was installed in more than 165 countries in 2015"
  					 ];
  
      /**
          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 installer
  	function calculateProgress(steps, next) {
  		var sorted = _.sortBy(steps, "serverOrder");
  
  		var pct = "100%";
  		for (var i = sorted.length - 1; i >= 0; i--) {
  			if(sorted[i].name == next){
  				pct = Math.floor((i+1) / steps.length  * 100) + "%";
  				break;
  			}
  		}
  	    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)
  			        .success(function(data, status, headers, config) {
  			            if (!data.complete) {
  
  			                //progress feedback
  			                service.status.progress = calculateProgress(service.status.steps, data.nextStep);
  
  			                if (data.view) {
  			                    //set the current view and model to whatever the process returns, the view is responsible for retriggering install();
  			                    var v = resolveView(data.view);
  			                    service.status.current = { view: v, model: data.model };
  
  			                    //turn off loading bar and feedback
  			                    service.switchToConfiguration();
  			                }
  			                else {
  			                    var desc = getDescriptionForStepName(service.status.steps, data.nextStep);
  			                    if (desc) {
  			                        service.status.feedback = desc;
  			                    }
  			                    processInstallStep();
  			                }
  			            }
  			            else {
  			                service.complete();
  			            }
  			        }).error(function(data, status, headers, config) {
  			            //need to handle 500's separately, this will happen if something goes wrong outside
  			            // of the installer (like app startup events or something) and these will get returned as text/html
  			            // not as json. If this happens we can't actually load in external views since they will YSOD as well!
                          // so we need to display this in our own internal way
  			            
  			            if (status >= 500 && status < 600) {			                
  			                service.status.current = { view: "ysod", model: null };
  			                var ysod = data;
                              //we need to manually write the html to the iframe - the html contains full html markup
  			                $timeout(function () {
  			                    document.getElementById('ysod').contentDocument.write(ysod);
  			                }, 500);
  			            }
  			            else {
  			                //this is where we handle installer error
  			                var v = data.view ? resolveView(data.view) : resolveView("error");
  			                var model = data.model ? data.model : 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;
  });
  
  angular.module("umbraco.install").controller("Umbraco.Installer.DataBaseController", function($scope, $http, installerService){
  	
  	$scope.checking = false;
  	$scope.dbs = [
  					{name: 'Microsoft SQL Server Compact (SQL CE)', id: 0},
  					{name: 'Microsoft SQL Server', id: 1},
                      { name: 'Microsoft SQL Azure', id: 3 },
                      { name: 'MySQL', id: 2 },
  					{name: 'Custom connection string', id: -1}];
  
  	if(installerService.status.current.model.dbType === undefined){
  		installerService.status.current.model.dbType = 0;
  	}
  	
      $scope.validateAndForward = function(){
  		if(!$scope.checking && this.myForm.$valid){
  		 	$scope.checking = true;
  			var model = installerService.status.current.model;
  
  			$http.post(Umbraco.Sys.ServerVariables.installApiBaseUrl + "PostValidateDatabaseConnection",
  				model).then(function(response){
  					
  					if(response.data === "true"){
  						installerService.forward();	
  					}else{
  						$scope.invalidDbDns = true;
  					}
  
  					$scope.checking = false;
  			}, function(){
  				$scope.invalidDbDns = true;
  				$scope.checking = false;
  			});
  		}
  	};
  });
  angular.module("umbraco.install").controller("Umbraco.Installer.PackagesController", function ($scope, installerService) {
  
      installerService.getPackages().then(function (response) {
          $scope.packages = response.data;
      });
  
      $scope.setPackageAndContinue = function (pckId) {
          installerService.status.current.model = pckId;
          installerService.forward();
      };
  
  });
  angular.module("umbraco.install").controller("Umbraco.Install.UserController", function($scope, installerService) {
      
      $scope.passwordPattern = /.*/;
      $scope.installer.current.model.subscribeToNewsLetter = true;
      
      if ($scope.installer.current.model.minNonAlphaNumericLength > 0) {
          var exp = "";
          for (var i = 0; i < $scope.installer.current.model.minNonAlphaNumericLength; i++) {
              exp += ".*[\\W].*";
          }
          //replace duplicates
          exp = exp.replace(".*.*", ".*");            
          $scope.passwordPattern = new RegExp(exp);
      }
  
  	$scope.validateAndInstall = function(){
  			installerService.install();
  	};
  
  	$scope.validateAndForward = function(){
  			if(this.myForm.$valid){
  				installerService.forward();
  			}
  	};
  	
  });
  
  })();