<commit_before>module.exports = {
    startAsync: function (success, fail, args) {
        try {
            var uri = new Windows.Foundation.Uri(args[0]),
                resultFilePath = args[1];

            var completeHandler = function() {
                success();
            };
            var errorHandler = function(err) {
                fail(err);
            };
            var progressHandler = function (operation) {

                var progress = 100 * operation.progress.bytesReceived / operation.progress.totalBytesToReceive;

                success({
                    progress: progress,
                    keepCallback: true
                });
            };

            var downloadLocation;

            var downloadPromise = Windows.Storage.StorageFile.getFileFromPathAsync(resultFilePath).then(
                function (file) {
                    downloadLocation = file;
                }, errorHandler).then(function () {
                    return Windows.Networking.BackgroundTransfer.BackgroundDownloader.getCurrentDownloadsAsync();
                }, errorHandler).then(function (downloads) {
                    // After app termination, an app should enumerate all existing DownloadOperation instances at next start-up using
                    // GetCurrentDownloadsAsync. When a Windows Store app using Background Transfer is terminated, incomplete downloads
                    // will persist in the background. If the app is restarted after termination and operations from the previous
                    // session are not enumerated and re-attached to the current session, they will remain incomplete and continue to occupy resources
                    // http://msdn.microsoft.com/library/windows/apps/br207126
                    for (var i = 0; i < downloads.size; i++) {
                        if (downloads[i].requestedUri.absoluteUri == uri.absoluteUri) {
                            // resume download
                            return downloads[i].attachAsync();
                        }
                    }
                    // new download
                    return Windows.Networking.BackgroundTransfer.BackgroundDownloader().createDownload(uri, downloadLocation).startAsync();
                }, errorHandler);

            // attach callbacks
            downloadPromise.then(completeHandler, errorHandler, progressHandler);
            // save operation promise to be able to stop it later
            BackgroundTransfer.activeDownloads = BackgroundTransfer.activeDownloads || [];
            BackgroundTransfer.activeDownloads[uri.absoluteUri] = downloadPromise;

        } catch(ex) {
            fail(ex);
        }

    },
    stop: function (success, fail, args) {
        try {
            var uri = new Windows.Foundation.Uri(args[0]);

            if (BackgroundTransfer.activeDownloads && BackgroundTransfer.activeDownloads[uri.absoluteUri]) {
                BackgroundTransfer.activeDownloads[uri.absoluteUri].cancel();
                BackgroundTransfer.activeDownloads[uri.absoluteUri] = null;
            }

            success();

        } catch(ex) {
            fail(ex);
        }
    }
};
require("cordova/windows8/commandProxy").add("BackgroundDownload", module.exports);
<commit_msg>windows: more detailed progress info<commit_after>module.exports = {
    startAsync: function (success, fail, args) {
        try {
            var uri = new Windows.Foundation.Uri(args[0]),
                resultFilePath = args[1];

            var completeHandler = function() {
                success();
            };
            var errorHandler = function(err) {
                fail(err);
            };
            var progressHandler = function (operation) {

                var progress = {
                    bytesReceived: operation.progress.bytesReceived,
                    totalBytesToReceive: operation.progress.totalBytesToReceive
                };

                success({
                    progress: progress,
                    keepCallback: true
                });
            };

            var downloadLocation;

            var downloadPromise = Windows.Storage.StorageFile.getFileFromPathAsync(resultFilePath).then(
                function (file) {
                    downloadLocation = file;
                }, errorHandler).then(function () {
                    return Windows.Networking.BackgroundTransfer.BackgroundDownloader.getCurrentDownloadsAsync();
                }, errorHandler).then(function (downloads) {
                    // After app termination, an app should enumerate all existing DownloadOperation instances at next start-up using
                    // GetCurrentDownloadsAsync. When a Windows Store app using Background Transfer is terminated, incomplete downloads
                    // will persist in the background. If the app is restarted after termination and operations from the previous
                    // session are not enumerated and re-attached to the current session, they will remain incomplete and continue to occupy resources
                    // http://msdn.microsoft.com/library/windows/apps/br207126
                    for (var i = 0; i < downloads.size; i++) {
                        if (downloads[i].requestedUri.absoluteUri == uri.absoluteUri) {
                            // resume download
                            return downloads[i].attachAsync();
                        }
                    }
                    // new download
                    return Windows.Networking.BackgroundTransfer.BackgroundDownloader().createDownload(uri, downloadLocation).startAsync();
                }, errorHandler);

            // attach callbacks
            downloadPromise.then(completeHandler, errorHandler, progressHandler);
            // save operation promise to be able to stop it later
            BackgroundTransfer.activeDownloads = BackgroundTransfer.activeDownloads || [];
            BackgroundTransfer.activeDownloads[uri.absoluteUri] = downloadPromise;

        } catch(ex) {
            fail(ex);
        }

    },
    stop: function (success, fail, args) {
        try {
            var uri = new Windows.Foundation.Uri(args[0]);

            if (BackgroundTransfer.activeDownloads && BackgroundTransfer.activeDownloads[uri.absoluteUri]) {
                BackgroundTransfer.activeDownloads[uri.absoluteUri].cancel();
                BackgroundTransfer.activeDownloads[uri.absoluteUri] = null;
            }

            success();

        } catch(ex) {
            fail(ex);
        }
    }
};
require("cordova/windows8/commandProxy").add("BackgroundDownload", module.exports);
<|endoftext|>


<commit_before>'use strict';

var cdApp = soajsApp.components;
cdApp.controller('cdAppCtrl', ['$scope', '$timeout', '$modal', '$cookies', 'ngDataApi', 'injectFiles', function ($scope, $timeout, $modal, $cookies, ngDataApi, injectFiles) {
	$scope.$parent.isUserLoggedIn();
	$scope.configuration={};
	$scope.access = {};
	constructModulePermissions($scope, $scope.access, cdAppConfig.permissions);
	$scope.$parent.rerenderMenuAfterEnvExclude(cdNav);

	$scope.cdData = {};
	$scope.myEnv = $cookies.getObject('myEnv').code;
	$scope.upgradeSpaceLink = cdAppConfig.upgradeSpaceLink;
	$scope.updateCount;
	$scope.upgradeCount;

	$scope.cdShowHide = function(oneSrv){
		if($scope.configuration[oneSrv].icon === 'minus'){
			$scope.configuration[oneSrv].icon = 'plus';
			jQuery('#cdc_' + oneSrv).slideUp();
		}
		else{
			$scope.configuration[oneSrv].icon = 'minus';
			jQuery('#cdc_' + oneSrv).slideDown()
		}
	};

	$scope.getRecipe = function () {

		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'get',
			routeName: '/dashboard/cd'
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}

			if(!response) {
				response = {};
			}

			$scope.cdData = response;
			$scope.maxEntries = 0;
			if(response[$scope.myEnv.toUpperCase()]){
				$scope.configuration = angular.copy(response[$scope.myEnv.toUpperCase()]);
				if(Object.hasOwnProperty.call($scope.configuration, 'pause')){
					$scope.paused = $scope.configuration.pause;
				}
				delete $scope.configuration.pause;
				for(var service in $scope.configuration){
					if(SOAJSRMS.indexOf("soajs." + service) !== -1) {
						delete $scope.configuration[service];
					}
					else{
						if(['pause', 'deploy', 'options'].indexOf(service) === -1){
							$scope.maxEntries++;
							$scope.configuration[service].icon = 'minus';
							$scope.configuration[service].versions = {};
							for(var i in $scope.configuration[service]){
								if (['branch', 'strategy', 'versions', 'icon', 'deploy', 'options'].indexOf(i) === -1) {
									$scope.configuration[service].versions[i] = angular.copy($scope.configuration[service][i]);
									delete $scope.configuration[service][i];
								}
							}
							if (Object.keys($scope.configuration[service].versions).length === 0) {
								delete $scope.configuration[service].versions;
							}
						}
					}
				}
			}
		});
	};

	$scope.pauseRecipe = function(pause){
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'post',
			routeName: '/dashboard/cd/pause',
			data: {
				"config": {
					"env": $scope.myEnv,
					"pause": pause
				}
			}
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}
			else {
				$scope.displayAlert('success', 'Recipe Saved successfully');
				$timeout(function(){
					$scope.getRecipe();
				}, 200)
			}
		});
	};

	$scope.saveRecipe = function(service, version) {
		var data={
			env: $scope.myEnv,
			serviceName: service
		};

		if(SOAJSRMS.indexOf("soajs." + service) !== -1){
			$scope.displayAlert('danger', "You cannot Apply Continuous Delivery on a SOAJS Ready Made Service.");
		}

		if ($scope.configuration[service].versions && Object.keys($scope.configuration[service].versions).length > 0) {
			data.version = {
				v: version
			};

			for (var i in $scope.configuration[service].versions[version]) {
				data.version[i] = $scope.configuration[service].versions[version][i];
			}
		}
		else {
			data.default = {
				branch: $scope.configuration[service].branch,
				strategy: $scope.configuration[service].strategy
			};
			if ($scope.configuration[service].deploy) {
				data.default.deploy = $scope.configuration[service].deploy;
				data.default.options = $scope.configuration[service].options;
			}
		}

		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'post',
			routeName: '/dashboard/cd',
			data: {
				"config": data
			}
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}
			else {
				$scope.displayAlert('success', 'Recipe Saved successfully');
				$scope.getRecipe();
			}
		});
	};

	$scope.getUpdates = function () {
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'get',
			routeName: '/dashboard/cd/updates',
			params: {
				"env": $scope.myEnv
			}
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}
			else {
				parseMyResponse(response);
			}
		});

		function parseMyResponse(list) {
			$scope.imageLedger = [];
			$scope.catalogLedger = [];

			$scope.upgradeCount =0;

			list.forEach(function (oneEntry) {
				$scope.upgradeCount++;
				if($scope.myEnv.toLowerCase() === 'dashboard'){
					oneEntry.rms = true;
				}
				else if(oneEntry.labels && oneEntry.labels['soajs.content'] === 'true' && oneEntry.labels['soajs.service.name']){
					oneEntry.rms = true;
				}
				switch (oneEntry.mode) {
					case 'image':
						$scope.imageLedger.push(oneEntry);
						break;
					case 'rebuild':
						$scope.catalogLedger.push(oneEntry);
						break;
				}
			});
			if($scope.upgradeCount > 0){
				$scope.upgradeCount = "(" + $scope.upgradeCount + ")";
			}
			else{
				$scope.upgradeCount = null;
			}
		}
	};

	$scope.getLedger = function () {
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'get',
			routeName: '/dashboard/cd/ledger',
			params: {
				"env": $scope.myEnv
			}
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}
			else {
				$scope.ledger = response;
				$scope.updateCount = 0;
				$scope.ledger.forEach(function(oneLedgerEntry){
					if(oneLedgerEntry.notify && !oneLedgerEntry.manual){
						$scope.updateCount++;
					}
				});
				if($scope.updateCount > 0){
					$scope.updateCount = "(" + $scope.updateCount + ")";
				}
				else{
					$scope.updateCount = null;
				}
			}
		});
	};

	$scope.updateEntry = function (oneEntry, operation) {
		var formConfig = {
			entries: []
		};

		if (operation === 'redeploy') {
			doRebuild(null);
		}
		else {
			//testing/////////
			oneEntry.catalog = {
				"image": {},
				"envs": {}
			};
			//testing/////////
			if (oneEntry.catalog.image && oneEntry.catalog.image.override) {
				//append images
				formConfig.entries.push({
					'name': "ImagePrefix",
					'label': "Image Prefix",
					'type': 'text',
					'value': oneEntry.catalog.image.prefix,
					'fieldMsg': "Override the image prefix if you want"
				});

				formConfig.entries.push({
					'name': "ImageName",
					'label': "Image Name",
					'type': 'text',
					'value': oneEntry.catalog.image.name,
					'fieldMsg': "Override the image name if you want"
				});

				formConfig.entries.push({
					'name': "ImageTag",
					'label': "Image Tag",
					'type': 'text',
					'value': oneEntry.catalog.image.tag,
					'fieldMsg': "Override the image tag if you want"
				});
			}

			//append inputs whose type is userInput
			if (oneEntry.catalog.envs) {
				for (var envVariable in oneEntry.catalog.envs) {
					if (oneEntry.catalog.envs[envVariable].type === 'userInput') {

						var defaultValue = oneEntry.catalog.envs[envVariable].default || '';
						//todo: get value from service.env
						oneEntry.service.env.forEach(function (oneEnv) {
							if (oneEnv.indexOf(envVariable) !== -1) {
								defaultValue = oneEnv.split("=")[1];
							}
						});

						//push a new input for this variable
						var newInput = {
							'name': '_ci_' + envVariable,
							'label': oneEntry.catalog.envs[envVariable].label || envVariable,
							'type': 'text',
							'value': defaultValue,
							'fieldMsg': oneEntry.catalog.envs[envVariable].fieldMsg
						};

						if (!defaultValue || defaultValue === '') {
							newInput.required = true;
						}

						formConfig.entries.push(newInput);
					}
				}
			}

			if (formConfig.entries.length === 0) {
				doRebuild(null);
			}
			else {
				var options = {
					timeout: $timeout,
					form: formConfig,
					name: 'rebuildService',
					label: 'Rebuild Service',
					actions: [
						{
							'type': 'submit',
							'label': translation.submit[LANG],
							'btn': 'primary',
							'action': function (formData) {
								doRebuild(formData);
							}
						},
						{
							'type': 'reset',
							'label': translation.cancel[LANG],
							'btn': 'danger',
							'action': function () {
								$scope.modalInstance.dismiss('cancel');
								$scope.form.formData = {};
							}
						}
					]
				};
				buildFormWithModal($scope, $modal, options);
			}
		}

		function doRebuild(formData) {
			var params;
			if(operation === 'redeploy'){
				params = {
					data:{
						id: oneEntry._id.toString(),
						action: operation
					}
				}
			}
			else if (operation === 'rebuild') {
				params = {
					data:{
						env: $scope.myEnv.toUpperCase(),
						serviceId: oneEntry.id || oneEntry.serviceId,
						serviceName: oneEntry.labels['soajs.service.name'],
						serviceVersion: oneEntry.labels['soajs.service.version'] || null,
						mode: (oneEntry.labels && oneEntry.labels['soajs.service.mode']) ? oneEntry.labels['soajs.service.mode'] : oneEntry.mode,
						action: operation
					}
				};

				if (formData && Object.keys(formData).length > 0) {
					//inject user input catalog entry and image override
					params.custom = {
						image: {
							name: formData['ImageName'],
							prefix: formData['ImagePrefix'],
							tag: formData['ImageTag']
						}
					};

					for (var input in formData) {
						if (input.indexOf('_ci_') !== -1) {
							if (!params.custom.env) {
								params.custom.env = {};
							}
							params.custom.env[input.replace('_ci_', '')] = formData[input];
						}
					}
				}
			}
			else if (operation === 'deploy') {
				params = {
					data: {
						env: oneEntry.env,
						serviceName: oneEntry.serviceName,
						id: oneEntry._id.toString(),
						action: operation
					},
					deployOptions: oneEntry.deployOptions
				};

				if (oneEntry.serviceVersion) {
					params.data.serviceVersion = oneEntry.serviceVersion;
				}

				if (formData && Object.keys(formData).length > 0) {
					//inject user input catalog entry and image override
					if (!params.deployOptions.custom) {
						params.deployOptions.custom = {};
					}

					params.deployOptions.custom.image = {
						name: formData['ImageName'],
						prefix: formData['ImagePrefix'],
						tag: formData['ImageTag']
					};

					for (var input in formData) {
						if (input.indexOf('_ci_') !== -1) {
							if (!params.deployOptions.custom.env) {
								params.deployOptions.custom.env = {};
							}
							params.deployOptions.custom.env[input.replace('_ci_', '')] = formData[input];
						}
					}
				}
			}

			overlayLoading.show();
			getSendDataFromServer($scope, ngDataApi, {
				method: 'put',
				routeName: '/dashboard/cd/action',
				data: params
			}, function (error, response) {
				overlayLoading.hide();
				if (error) {
					$scope.displayAlert('danger', error.message);
				}
				else {
					$scope.displayAlert('success', 'Service operation [' + operation + '] was successful');

					if(operation === 'redeploy' || operation === 'deploy'){
						$scope.getLedger();
					}
					else{
						$scope.getUpdates();
					}
					overlayLoading.hide();
					if($scope.modalInstance){
						$scope.modalInstance.dismiss();
					}
				}
			});
		}
	};

	$scope.deployEntry = function (oneEntry) {
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'post',
			routeName: '/dashboard/cd/action',
			data: oneEntry.deployOptions || {}
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}
			else {
				$scope.displayAlert('success', 'Service deployed successfully');

				if(operation === 'redeploy'){
					$scope.getLedger();
				}
				else{
					$scope.getUpdates();
				}
			}
		});
	};

	$scope.readAll = function () {
        overlayLoading.show();
        getSendDataFromServer($scope, ngDataApi, {
            method: 'put',
            routeName: '/dashboard/cd/ledger/read',
            data: {
            	"data": {"all": true}
            }

        }, function (error, response) {
            overlayLoading.hide();
            if (error) {
                $scope.displayAlert('danger', error.message);
            }
            else {
                $scope.displayAlert('success', 'All entries updated');
                $scope.getLedger();
            }
        });
	};

    $scope.readOne = function (oneEntry) {
        overlayLoading.show();
        getSendDataFromServer($scope, ngDataApi, {
            method: 'put',
            routeName: '/dashboard/cd/ledger/read',
            data: {
            	"data": {"id": oneEntry._id}
            }

        }, function (error, response) {
            overlayLoading.hide();
            if (error) {
                $scope.displayAlert('danger', error.message);
            }
            else {
                $scope.displayAlert('success', 'Entry updated');
	            $scope.getLedger();
            }
        });
    };

	injectFiles.injectCss("modules/dashboard/cd/cd.css");

	// Start here
	if ($scope.access.get) {
		$scope.getLedger();
		$scope.getUpdates();
	}
}]);
<commit_msg>Minor UI bug fix in Updates & Upgrades section<commit_after>'use strict';

var cdApp = soajsApp.components;
cdApp.controller('cdAppCtrl', ['$scope', '$timeout', '$modal', '$cookies', 'ngDataApi', 'injectFiles', function ($scope, $timeout, $modal, $cookies, ngDataApi, injectFiles) {
	$scope.$parent.isUserLoggedIn();
	$scope.configuration={};
	$scope.access = {};
	constructModulePermissions($scope, $scope.access, cdAppConfig.permissions);
	$scope.$parent.rerenderMenuAfterEnvExclude(cdNav);

	$scope.cdData = {};
	$scope.myEnv = $cookies.getObject('myEnv').code;
	$scope.upgradeSpaceLink = cdAppConfig.upgradeSpaceLink;
	$scope.updateCount;
	$scope.upgradeCount;

	$scope.cdShowHide = function(oneSrv){
		if($scope.configuration[oneSrv].icon === 'minus'){
			$scope.configuration[oneSrv].icon = 'plus';
			jQuery('#cdc_' + oneSrv).slideUp();
		}
		else{
			$scope.configuration[oneSrv].icon = 'minus';
			jQuery('#cdc_' + oneSrv).slideDown()
		}
	};

	$scope.getRecipe = function () {

		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'get',
			routeName: '/dashboard/cd'
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}

			if(!response) {
				response = {};
			}

			$scope.cdData = response;
			$scope.maxEntries = 0;
			if(response[$scope.myEnv.toUpperCase()]){
				$scope.configuration = angular.copy(response[$scope.myEnv.toUpperCase()]);
				if(Object.hasOwnProperty.call($scope.configuration, 'pause')){
					$scope.paused = $scope.configuration.pause;
				}
				delete $scope.configuration.pause;
				for(var service in $scope.configuration){
					if(SOAJSRMS.indexOf("soajs." + service) !== -1) {
						delete $scope.configuration[service];
					}
					else{
						if(['pause', 'deploy', 'options'].indexOf(service) === -1){
							$scope.maxEntries++;
							$scope.configuration[service].icon = 'minus';
							$scope.configuration[service].versions = {};
							for(var i in $scope.configuration[service]){
								if (['branch', 'strategy', 'versions', 'icon', 'deploy', 'options'].indexOf(i) === -1) {
									$scope.configuration[service].versions[i] = angular.copy($scope.configuration[service][i]);
									delete $scope.configuration[service][i];
								}
							}
							if (Object.keys($scope.configuration[service].versions).length === 0) {
								delete $scope.configuration[service].versions;
							}
						}
					}
				}
			}
		});
	};

	$scope.pauseRecipe = function(pause){
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'post',
			routeName: '/dashboard/cd/pause',
			data: {
				"config": {
					"env": $scope.myEnv,
					"pause": pause
				}
			}
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}
			else {
				$scope.displayAlert('success', 'Recipe Saved successfully');
				$timeout(function(){
					$scope.getRecipe();
				}, 200)
			}
		});
	};

	$scope.saveRecipe = function(service, version) {
		var data={
			env: $scope.myEnv,
			serviceName: service
		};

		if(SOAJSRMS.indexOf("soajs." + service) !== -1){
			$scope.displayAlert('danger', "You cannot Apply Continuous Delivery on a SOAJS Ready Made Service.");
		}

		if ($scope.configuration[service].versions && Object.keys($scope.configuration[service].versions).length > 0) {
			data.version = {
				v: version
			};

			for (var i in $scope.configuration[service].versions[version]) {
				data.version[i] = $scope.configuration[service].versions[version][i];
			}
		}
		else {
			data.default = {
				branch: $scope.configuration[service].branch,
				strategy: $scope.configuration[service].strategy
			};
			if ($scope.configuration[service].deploy) {
				data.default.deploy = $scope.configuration[service].deploy;
				data.default.options = $scope.configuration[service].options;
			}
		}

		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'post',
			routeName: '/dashboard/cd',
			data: {
				"config": data
			}
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}
			else {
				$scope.displayAlert('success', 'Recipe Saved successfully');
				$scope.getRecipe();
			}
		});
	};

	$scope.getUpdates = function () {
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'get',
			routeName: '/dashboard/cd/updates',
			params: {
				"env": $scope.myEnv
			}
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}
			else {
				parseMyResponse(response);
			}
		});

		function parseMyResponse(list) {
			$scope.imageLedger = [];
			$scope.catalogLedger = [];

			$scope.upgradeCount =0;

			list.forEach(function (oneEntry) {
				$scope.upgradeCount++;
				if($scope.myEnv.toLowerCase() === 'dashboard'){
					oneEntry.rms = true;
				}
				else if(oneEntry.labels && oneEntry.labels['soajs.content'] === 'true' && oneEntry.labels['soajs.service.name']){
					oneEntry.rms = true;
				}
				switch (oneEntry.mode) {
					case 'image':
						$scope.imageLedger.push(oneEntry);
						break;
					case 'rebuild':
						$scope.catalogLedger.push(oneEntry);
						break;
				}
			});
			if($scope.upgradeCount > 0){
				$scope.upgradeCount = "(" + $scope.upgradeCount + ")";
			}
			else{
				$scope.upgradeCount = null;
			}
		}
	};

	$scope.getLedger = function () {
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'get',
			routeName: '/dashboard/cd/ledger',
			params: {
				"env": $scope.myEnv
			}
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}
			else {
				$scope.ledger = response;
				$scope.updateCount = 0;
				$scope.ledger.forEach(function(oneLedgerEntry){
					if(oneLedgerEntry.notify && !oneLedgerEntry.manual && !oneLedgerEntry.read){
						$scope.updateCount++;
					}
				});
				if($scope.updateCount > 0){
					$scope.updateCount = "(" + $scope.updateCount + ")";
				}
				else{
					$scope.updateCount = null;
				}
			}
		});
	};

	$scope.updateEntry = function (oneEntry, operation) {
		var formConfig = {
			entries: []
		};

		if (operation === 'redeploy') {
			doRebuild(null);
		}
		else {
			//testing/////////
			oneEntry.catalog = {
				"image": {},
				"envs": {}
			};
			//testing/////////
			if (oneEntry.catalog.image && oneEntry.catalog.image.override) {
				//append images
				formConfig.entries.push({
					'name': "ImagePrefix",
					'label': "Image Prefix",
					'type': 'text',
					'value': oneEntry.catalog.image.prefix,
					'fieldMsg': "Override the image prefix if you want"
				});

				formConfig.entries.push({
					'name': "ImageName",
					'label': "Image Name",
					'type': 'text',
					'value': oneEntry.catalog.image.name,
					'fieldMsg': "Override the image name if you want"
				});

				formConfig.entries.push({
					'name': "ImageTag",
					'label': "Image Tag",
					'type': 'text',
					'value': oneEntry.catalog.image.tag,
					'fieldMsg': "Override the image tag if you want"
				});
			}

			//append inputs whose type is userInput
			if (oneEntry.catalog.envs) {
				for (var envVariable in oneEntry.catalog.envs) {
					if (oneEntry.catalog.envs[envVariable].type === 'userInput') {

						var defaultValue = oneEntry.catalog.envs[envVariable].default || '';
						//todo: get value from service.env
						oneEntry.service.env.forEach(function (oneEnv) {
							if (oneEnv.indexOf(envVariable) !== -1) {
								defaultValue = oneEnv.split("=")[1];
							}
						});

						//push a new input for this variable
						var newInput = {
							'name': '_ci_' + envVariable,
							'label': oneEntry.catalog.envs[envVariable].label || envVariable,
							'type': 'text',
							'value': defaultValue,
							'fieldMsg': oneEntry.catalog.envs[envVariable].fieldMsg
						};

						if (!defaultValue || defaultValue === '') {
							newInput.required = true;
						}

						formConfig.entries.push(newInput);
					}
				}
			}

			if (formConfig.entries.length === 0) {
				doRebuild(null);
			}
			else {
				var options = {
					timeout: $timeout,
					form: formConfig,
					name: 'rebuildService',
					label: 'Rebuild Service',
					actions: [
						{
							'type': 'submit',
							'label': translation.submit[LANG],
							'btn': 'primary',
							'action': function (formData) {
								doRebuild(formData);
							}
						},
						{
							'type': 'reset',
							'label': translation.cancel[LANG],
							'btn': 'danger',
							'action': function () {
								$scope.modalInstance.dismiss('cancel');
								$scope.form.formData = {};
							}
						}
					]
				};
				buildFormWithModal($scope, $modal, options);
			}
		}

		function doRebuild(formData) {
			var params;
			if(operation === 'redeploy'){
				params = {
					data:{
						id: oneEntry._id.toString(),
						action: operation
					}
				}
			}
			else if (operation === 'rebuild') {
				params = {
					data:{
						env: $scope.myEnv.toUpperCase(),
						serviceId: oneEntry.id || oneEntry.serviceId,
						serviceName: oneEntry.labels['soajs.service.name'],
						serviceVersion: oneEntry.labels['soajs.service.version'] || null,
						mode: (oneEntry.labels && oneEntry.labels['soajs.service.mode']) ? oneEntry.labels['soajs.service.mode'] : oneEntry.mode,
						action: operation
					}
				};

				if (formData && Object.keys(formData).length > 0) {
					//inject user input catalog entry and image override
					params.custom = {
						image: {
							name: formData['ImageName'],
							prefix: formData['ImagePrefix'],
							tag: formData['ImageTag']
						}
					};

					for (var input in formData) {
						if (input.indexOf('_ci_') !== -1) {
							if (!params.custom.env) {
								params.custom.env = {};
							}
							params.custom.env[input.replace('_ci_', '')] = formData[input];
						}
					}
				}
			}
			else if (operation === 'deploy') {
				params = {
					data: {
						env: oneEntry.env,
						serviceName: oneEntry.serviceName,
						id: oneEntry._id.toString(),
						action: operation
					},
					deployOptions: oneEntry.deployOptions
				};

				if (oneEntry.serviceVersion) {
					params.data.serviceVersion = oneEntry.serviceVersion;
				}

				if (formData && Object.keys(formData).length > 0) {
					//inject user input catalog entry and image override
					if (!params.deployOptions.custom) {
						params.deployOptions.custom = {};
					}

					params.deployOptions.custom.image = {
						name: formData['ImageName'],
						prefix: formData['ImagePrefix'],
						tag: formData['ImageTag']
					};

					for (var input in formData) {
						if (input.indexOf('_ci_') !== -1) {
							if (!params.deployOptions.custom.env) {
								params.deployOptions.custom.env = {};
							}
							params.deployOptions.custom.env[input.replace('_ci_', '')] = formData[input];
						}
					}
				}
			}

			overlayLoading.show();
			getSendDataFromServer($scope, ngDataApi, {
				method: 'put',
				routeName: '/dashboard/cd/action',
				data: params
			}, function (error, response) {
				overlayLoading.hide();
				if (error) {
					$scope.displayAlert('danger', error.message);
				}
				else {
					$scope.displayAlert('success', 'Service operation [' + operation + '] was successful');

					if(operation === 'redeploy' || operation === 'deploy'){
						$scope.getLedger();
					}
					else{
						$scope.getUpdates();
					}
					overlayLoading.hide();
					if($scope.modalInstance){
						$scope.modalInstance.dismiss();
					}
				}
			});
		}
	};

	$scope.deployEntry = function (oneEntry) {
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'post',
			routeName: '/dashboard/cd/action',
			data: oneEntry.deployOptions || {}
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}
			else {
				$scope.displayAlert('success', 'Service deployed successfully');

				if(operation === 'redeploy'){
					$scope.getLedger();
				}
				else{
					$scope.getUpdates();
				}
			}
		});
	};

	$scope.readAll = function () {
        overlayLoading.show();
        getSendDataFromServer($scope, ngDataApi, {
            method: 'put',
            routeName: '/dashboard/cd/ledger/read',
            data: {
            	"data": {"all": true}
            }

        }, function (error, response) {
            overlayLoading.hide();
            if (error) {
                $scope.displayAlert('danger', error.message);
            }
            else {
                $scope.displayAlert('success', 'All entries updated');
                $scope.getLedger();
            }
        });
	};

    $scope.readOne = function (oneEntry) {
        overlayLoading.show();
        getSendDataFromServer($scope, ngDataApi, {
            method: 'put',
            routeName: '/dashboard/cd/ledger/read',
            data: {
            	"data": {"id": oneEntry._id}
            }

        }, function (error, response) {
            overlayLoading.hide();
            if (error) {
                $scope.displayAlert('danger', error.message);
            }
            else {
                $scope.displayAlert('success', 'Entry updated');
	            $scope.getLedger();
            }
        });
    };

	injectFiles.injectCss("modules/dashboard/cd/cd.css");

	// Start here
	if ($scope.access.get) {
		$scope.getLedger();
		$scope.getUpdates();
	}
}]);
<|endoftext|>


<commit_before>/*global window */
import d3 from 'd3';
import fc from 'd3fc';
import chart from './chart/chart';
import model from './model/model';
import menu from './menu/menu';
import util from './util/util';
import event from './event';
import dataInterface from './data/dataInterface';
import coinbaseProducts from './data/coinbase/getProducts';
import coinbaseAdaptor from './data/adaptor/coinbase';
import dataGeneratorAdaptor from './data/adaptor/dataGenerator';
import quandlAdaptor from './data/adaptor/quandl';
import webSocket from './data/coinbase/webSocket';
import formatProducts from './data/coinbase/formatProducts';

export default function() {

    var app = {};

    var appContainer = d3.select('#app-container');
    var chartsContainer = appContainer.select('#charts-container');
    var overlay = appContainer.select('#overlay');
    var containers = {
        app: appContainer,
        charts: chartsContainer,
        primary: chartsContainer.select('#primary-container'),
        secondaries: chartsContainer.selectAll('.secondary-container'),
        xAxis: chartsContainer.select('#x-axis-container'),
        navbar: chartsContainer.select('#navbar-container'),
        overlay: overlay,
        overlaySecondaries: overlay. selectAll('.overlay-secondary-container'),
        legend: appContainer.select('#legend'),
        suspendLayout: function(value) {
            var self = this;
            Object.keys(self).forEach(function(key) {
                if (typeof self[key] !== 'function') {
                    self[key].layoutSuspended(value);
                }
            });
        }
    };

    var day1 = model.data.period({
        display: 'Daily',
        seconds: 86400,
        d3TimeInterval: {unit: d3.time.day, value: 1},
        timeFormat: '%b %d'});
    var week1 = model.data.period({
        display: 'Weekly',
        seconds: 60 * 60 * 24 * 7,
        d3TimeInterval: {unit: d3.time.week, value: 1},
        timeFormat: '%b %d'});
    var hour1 = model.data.period({
        display: '1 Hr',
        seconds: 3600,
        d3TimeInterval: {unit: d3.time.hour, value: 1},
        timeFormat: '%b %d %Hh'});
    var minute5 = model.data.period({
        display: '5 Min',
        seconds: 300,
        d3TimeInterval: {unit: d3.time.minute, value: 5},
        timeFormat: '%H:%M'});
    var minute1 = model.data.period({
        display: '1 Min',
        seconds: 60,
        d3TimeInterval: {unit: d3.time.minute, value: 1},
        timeFormat: '%H:%M'});

    var generatedSource = model.data.source(dataGeneratorAdaptor());
    var bitcoinSource = model.data.source(coinbaseAdaptor(), webSocket());
    var quandlSource = model.data.source(quandlAdaptor());

    var generated = model.data.product({
        id: null,
        display: 'Data Generator',
        volumeFormat: '.3s',
        periods: [day1],
        source: generatedSource
    });
    var quandl = model.data.product({
        id: 'GOOG',
        display: 'GOOG',
        volumeFormat: '.2f',
        periods: [day1, week1],
        source: quandlSource
    });

    var primaryChartModel = model.chart.primary(generated);
    var secondaryChartModel = model.chart.secondary(generated);
    var selectorsModel = model.menu.selectors();
    var xAxisModel = model.chart.xAxis(day1);
    var navModel = model.chart.nav();
    var navResetModel = model.chart.navigationReset();
    var headMenuModel = model.menu.head([generated, quandl], generated, day1);
    var legendModel = model.chart.legend(generated, day1);
    var overlayModel = model.menu.overlay();

    var charts = {
        primary: undefined,
        secondaries: [],
        xAxis: chart.xAxis(),
        navbar: undefined,
        legend: chart.legend()
    };

    var headMenu;
    var navReset;
    var selectors;

    function renderInternal() {
        if (layoutRedrawnInNextRender) {
            containers.suspendLayout(false);
        }

        containers.primary.datum(primaryChartModel)
            .call(charts.primary);

        containers.legend.datum(legendModel)
            .call(charts.legend);

        containers.secondaries.datum(secondaryChartModel)
            // TODO: Add component: group of secondary charts.
            // Then also move method layout.getSecondaryContainer into the group.
            .filter(function(d, i) { return i < charts.secondaries.length; })
            .each(function(d, i) {
                d3.select(this)
                    .attr('class', 'secondary-container ' + charts.secondaries[i].valueString)
                    .call(charts.secondaries[i].option);
            });

        containers.xAxis.datum(xAxisModel)
            .call(charts.xAxis);

        containers.navbar.datum(navModel)
            .call(charts.navbar);

        containers.app.select('#navbar-reset')
            .datum(navResetModel)
            .call(navReset);

        containers.app.select('.head-menu')
            .datum(headMenuModel)
            .call(headMenu);

        containers.app.select('#selectors')
            .datum(selectorsModel)
            .call(selectors);

        containers.overlay.datum(overlayModel)
            .call(overlay);

        if (layoutRedrawnInNextRender) {
            containers.suspendLayout(true);
            layoutRedrawnInNextRender = false;
        }
    }

    var render = fc.util.render(renderInternal);

    var layoutRedrawnInNextRender = true;

    function updateLayout() {
        layoutRedrawnInNextRender = true;
        util.layout(containers, charts);
    }

    function initialiseResize() {
        d3.select(window).on('resize', function() {
            updateLayout();
            render();
        });
    }

    function onViewChange(domain) {
        var viewDomain = [domain[0], domain[1]];
        primaryChartModel.viewDomain = viewDomain;
        secondaryChartModel.viewDomain = viewDomain;
        xAxisModel.viewDomain = viewDomain;
        navModel.viewDomain = viewDomain;

        var trackingLatest = util.domain.trackingLatestData(
            primaryChartModel.viewDomain,
            primaryChartModel.data);
        primaryChartModel.trackingLatest = trackingLatest;
        secondaryChartModel.trackingLatest = trackingLatest;
        navModel.trackingLatest = trackingLatest;
        navResetModel.trackingLatest = trackingLatest;
        render();
    }

    function onPrimaryIndicatorChange(indicator) {
        indicator.isSelected = !indicator.isSelected;
        updatePrimaryChartIndicators();
        render();
    }

    function onSecondaryChartChange(_chart) {
        _chart.isSelected = !_chart.isSelected;
        updateSecondaryCharts();
        render();
    }

    function onCrosshairChange(dataPoint) {
        legendModel.data = dataPoint;
        render();
    }

    function resetToLatest() {
        var data = primaryChartModel.data;
        var dataDomain = fc.util.extent()
            .fields('date')(data);
        var navTimeDomain = util.domain.moveToLatest(dataDomain, data, 0.2);
        onViewChange(navTimeDomain);
    }

    function loading(isLoading) {
        appContainer.select('#loading-message')
            .classed('hidden', !isLoading);
        appContainer.select('#charts')
            .classed('hidden', isLoading);
    }

    function updateModelData(data) {
        primaryChartModel.data = data;
        secondaryChartModel.data = data;
        navModel.data = data;
    }

    function updateModelSelectedProduct(product) {
        headMenuModel.selectedProduct = product;
        primaryChartModel.product = product;
        secondaryChartModel.product = product;
        legendModel.product = product;
    }

    function updateModelSelectedPeriod(period) {
        headMenuModel.selectedPeriod = period;
        xAxisModel.period = period;
        legendModel.period = period;
    }

    function initialisePrimaryChart() {
        return chart.primary()
            .on(event.crosshairChange, onCrosshairChange)
            .on(event.viewChange, onViewChange);
    }

    function initialiseNav() {
        return chart.nav()
            .on(event.viewChange, onViewChange);
    }

    function initialiseNavReset() {
        return menu.navigationReset()
            .on(event.resetToLatest, resetToLatest);
    }

    function initialiseDataInterface() {
        return dataInterface()
            .on(event.newTrade, function(data) {
                updateModelData(data);
                if (primaryChartModel.trackingLatest) {
                    var newDomain = util.domain.moveToLatest(
                        primaryChartModel.viewDomain,
                        primaryChartModel.data);
                    onViewChange(newDomain);
                }
            })
            .on(event.historicDataLoaded, function(data) {
                loading(false);
                updateModelData(data);
                legendModel.data = null;
                resetToLatest();
                updateLayout();
            })
            .on(event.historicFeedError, function(err) {
                console.log('Error getting historic data: ' + err); // TODO: something more useful for the user!
            })
            .on(event.streamingFeedError, function(err) {
                console.log('Error loading data from websocket: ' + err);
            });
    }

    function initialiseHeadMenu(_dataInterface) {
        return menu.head()
            .on(event.dataProductChange, function(product) {
                loading(true);
                updateModelSelectedProduct(product.option);
                updateModelSelectedPeriod(product.option.periods[0]);
                _dataInterface(product.option.periods[0].seconds, product.option);
                render();
            })
            .on(event.dataPeriodChange, function(period) {
                loading(true);
                updateModelSelectedPeriod(period.option);
                _dataInterface(period.option.seconds);
                render();
            })
            .on(event.clearAllPrimaryChartIndicatorsAndSecondaryCharts, function() {
                primaryChartModel.indicators.forEach(deselectOption);
                charts.secondaries.forEach(deselectOption);

                updatePrimaryChartIndicators();
                updateSecondaryCharts();
                render();
            });
    }

    function selectOption(option, options) {
        options.forEach(function(_option) {
            _option.isSelected = false;
        });
        option.isSelected = true;
    }

    function deselectOption(option) { option.isSelected = false; }

    function fetchCoinbaseProducts() {
        coinbaseProducts(insertProductsIntoHeadMenuModel);
    }

    function insertProductsIntoHeadMenuModel(error, bitcoinProducts) {
        if (error) {
            console.log('Error getting Coinbase products: ' + error); // TODO: something more useful for the user!
        } else {
            var defaultPeriods = [hour1, day1];
            var exceptionPeriods = d3.map();
            exceptionPeriods.set('BTC-USD', [minute1, minute5, hour1, day1]);
            var formattedProducts = formatProducts(bitcoinProducts, bitcoinSource, defaultPeriods, exceptionPeriods);
            headMenuModel.products = headMenuModel.products.concat(formattedProducts);
            render();
        }
    }

    function initialiseSelectors() {
        return menu.selectors()
            .on(event.primaryChartSeriesChange, function(series) {
                primaryChartModel.series = series;
                selectOption(series, selectorsModel.seriesSelector.options);
                render();
            })
            .on(event.primaryChartIndicatorChange, onPrimaryIndicatorChange)
            .on(event.secondaryChartChange, onSecondaryChartChange);
    }

    function updatePrimaryChartIndicators() {
        primaryChartModel.indicators =
            selectorsModel.indicatorSelector.indicatorOptions.filter(function(option) {
                return option.isSelected;
            });

        overlayModel.primaryIndicators = primaryChartModel.indicators;
    }

    function updateSecondaryCharts() {
        charts.secondaries =
            selectorsModel.indicatorSelector.secondaryChartOptions.filter(function(option) {
                return option.isSelected;
            });
        // TODO: This doesn't seem to be a concern of menu.
        charts.secondaries.forEach(function(chartOption) {
            chartOption.option.on(event.viewChange, onViewChange);
        });

        overlayModel.secondaryIndicators = charts.secondaries;
        // TODO: Remove .remove! (could a secondary chart group component manage this?).
        containers.secondaries.selectAll('*').remove();
        updateLayout();
    }

    function initialiseOverlay() {
        return menu.overlay()
            .on(event.primaryChartIndicatorChange, onPrimaryIndicatorChange)
            .on(event.secondaryChartChange, onSecondaryChartChange);
    }

    app.run = function() {
        charts.primary = initialisePrimaryChart();
        charts.navbar = initialiseNav();

        var _dataInterface = initialiseDataInterface();
        headMenu = initialiseHeadMenu(_dataInterface);
        navReset = initialiseNavReset();
        selectors = initialiseSelectors();
        overlay = initialiseOverlay();

        updateLayout();
        initialiseResize();

        _dataInterface(generated.periods[0].seconds, generated);
        fetchCoinbaseProducts();
    };

    return app;
}
<commit_msg>Update all seconds properties on period models to use calculation<commit_after>/*global window */
import d3 from 'd3';
import fc from 'd3fc';
import chart from './chart/chart';
import model from './model/model';
import menu from './menu/menu';
import util from './util/util';
import event from './event';
import dataInterface from './data/dataInterface';
import coinbaseProducts from './data/coinbase/getProducts';
import coinbaseAdaptor from './data/adaptor/coinbase';
import dataGeneratorAdaptor from './data/adaptor/dataGenerator';
import quandlAdaptor from './data/adaptor/quandl';
import webSocket from './data/coinbase/webSocket';
import formatProducts from './data/coinbase/formatProducts';

export default function() {

    var app = {};

    var appContainer = d3.select('#app-container');
    var chartsContainer = appContainer.select('#charts-container');
    var overlay = appContainer.select('#overlay');
    var containers = {
        app: appContainer,
        charts: chartsContainer,
        primary: chartsContainer.select('#primary-container'),
        secondaries: chartsContainer.selectAll('.secondary-container'),
        xAxis: chartsContainer.select('#x-axis-container'),
        navbar: chartsContainer.select('#navbar-container'),
        overlay: overlay,
        overlaySecondaries: overlay. selectAll('.overlay-secondary-container'),
        legend: appContainer.select('#legend'),
        suspendLayout: function(value) {
            var self = this;
            Object.keys(self).forEach(function(key) {
                if (typeof self[key] !== 'function') {
                    self[key].layoutSuspended(value);
                }
            });
        }
    };

    var week1 = model.data.period({
        display: 'Weekly',
        seconds: 60 * 60 * 24 * 7,
        d3TimeInterval: {unit: d3.time.week, value: 1},
        timeFormat: '%b %d'});
    var day1 = model.data.period({
        display: 'Daily',
        seconds: 60 * 60 * 24,
        d3TimeInterval: {unit: d3.time.day, value: 1},
        timeFormat: '%b %d'});
    var hour1 = model.data.period({
        display: '1 Hr',
        seconds: 60 * 60,
        d3TimeInterval: {unit: d3.time.hour, value: 1},
        timeFormat: '%b %d %Hh'});
    var minute5 = model.data.period({
        display: '5 Min',
        seconds: 60 * 5,
        d3TimeInterval: {unit: d3.time.minute, value: 5},
        timeFormat: '%H:%M'});
    var minute1 = model.data.period({
        display: '1 Min',
        seconds: 60,
        d3TimeInterval: {unit: d3.time.minute, value: 1},
        timeFormat: '%H:%M'});

    var generatedSource = model.data.source(dataGeneratorAdaptor());
    var bitcoinSource = model.data.source(coinbaseAdaptor(), webSocket());
    var quandlSource = model.data.source(quandlAdaptor());

    var generated = model.data.product({
        id: null,
        display: 'Data Generator',
        volumeFormat: '.3s',
        periods: [day1],
        source: generatedSource
    });
    var quandl = model.data.product({
        id: 'GOOG',
        display: 'GOOG',
        volumeFormat: '.2f',
        periods: [day1, week1],
        source: quandlSource
    });

    var primaryChartModel = model.chart.primary(generated);
    var secondaryChartModel = model.chart.secondary(generated);
    var selectorsModel = model.menu.selectors();
    var xAxisModel = model.chart.xAxis(day1);
    var navModel = model.chart.nav();
    var navResetModel = model.chart.navigationReset();
    var headMenuModel = model.menu.head([generated, quandl], generated, day1);
    var legendModel = model.chart.legend(generated, day1);
    var overlayModel = model.menu.overlay();

    var charts = {
        primary: undefined,
        secondaries: [],
        xAxis: chart.xAxis(),
        navbar: undefined,
        legend: chart.legend()
    };

    var headMenu;
    var navReset;
    var selectors;

    function renderInternal() {
        if (layoutRedrawnInNextRender) {
            containers.suspendLayout(false);
        }

        containers.primary.datum(primaryChartModel)
            .call(charts.primary);

        containers.legend.datum(legendModel)
            .call(charts.legend);

        containers.secondaries.datum(secondaryChartModel)
            // TODO: Add component: group of secondary charts.
            // Then also move method layout.getSecondaryContainer into the group.
            .filter(function(d, i) { return i < charts.secondaries.length; })
            .each(function(d, i) {
                d3.select(this)
                    .attr('class', 'secondary-container ' + charts.secondaries[i].valueString)
                    .call(charts.secondaries[i].option);
            });

        containers.xAxis.datum(xAxisModel)
            .call(charts.xAxis);

        containers.navbar.datum(navModel)
            .call(charts.navbar);

        containers.app.select('#navbar-reset')
            .datum(navResetModel)
            .call(navReset);

        containers.app.select('.head-menu')
            .datum(headMenuModel)
            .call(headMenu);

        containers.app.select('#selectors')
            .datum(selectorsModel)
            .call(selectors);

        containers.overlay.datum(overlayModel)
            .call(overlay);

        if (layoutRedrawnInNextRender) {
            containers.suspendLayout(true);
            layoutRedrawnInNextRender = false;
        }
    }

    var render = fc.util.render(renderInternal);

    var layoutRedrawnInNextRender = true;

    function updateLayout() {
        layoutRedrawnInNextRender = true;
        util.layout(containers, charts);
    }

    function initialiseResize() {
        d3.select(window).on('resize', function() {
            updateLayout();
            render();
        });
    }

    function onViewChange(domain) {
        var viewDomain = [domain[0], domain[1]];
        primaryChartModel.viewDomain = viewDomain;
        secondaryChartModel.viewDomain = viewDomain;
        xAxisModel.viewDomain = viewDomain;
        navModel.viewDomain = viewDomain;

        var trackingLatest = util.domain.trackingLatestData(
            primaryChartModel.viewDomain,
            primaryChartModel.data);
        primaryChartModel.trackingLatest = trackingLatest;
        secondaryChartModel.trackingLatest = trackingLatest;
        navModel.trackingLatest = trackingLatest;
        navResetModel.trackingLatest = trackingLatest;
        render();
    }

    function onPrimaryIndicatorChange(indicator) {
        indicator.isSelected = !indicator.isSelected;
        updatePrimaryChartIndicators();
        render();
    }

    function onSecondaryChartChange(_chart) {
        _chart.isSelected = !_chart.isSelected;
        updateSecondaryCharts();
        render();
    }

    function onCrosshairChange(dataPoint) {
        legendModel.data = dataPoint;
        render();
    }

    function resetToLatest() {
        var data = primaryChartModel.data;
        var dataDomain = fc.util.extent()
            .fields('date')(data);
        var navTimeDomain = util.domain.moveToLatest(dataDomain, data, 0.2);
        onViewChange(navTimeDomain);
    }

    function loading(isLoading) {
        appContainer.select('#loading-message')
            .classed('hidden', !isLoading);
        appContainer.select('#charts')
            .classed('hidden', isLoading);
    }

    function updateModelData(data) {
        primaryChartModel.data = data;
        secondaryChartModel.data = data;
        navModel.data = data;
    }

    function updateModelSelectedProduct(product) {
        headMenuModel.selectedProduct = product;
        primaryChartModel.product = product;
        secondaryChartModel.product = product;
        legendModel.product = product;
    }

    function updateModelSelectedPeriod(period) {
        headMenuModel.selectedPeriod = period;
        xAxisModel.period = period;
        legendModel.period = period;
    }

    function initialisePrimaryChart() {
        return chart.primary()
            .on(event.crosshairChange, onCrosshairChange)
            .on(event.viewChange, onViewChange);
    }

    function initialiseNav() {
        return chart.nav()
            .on(event.viewChange, onViewChange);
    }

    function initialiseNavReset() {
        return menu.navigationReset()
            .on(event.resetToLatest, resetToLatest);
    }

    function initialiseDataInterface() {
        return dataInterface()
            .on(event.newTrade, function(data) {
                updateModelData(data);
                if (primaryChartModel.trackingLatest) {
                    var newDomain = util.domain.moveToLatest(
                        primaryChartModel.viewDomain,
                        primaryChartModel.data);
                    onViewChange(newDomain);
                }
            })
            .on(event.historicDataLoaded, function(data) {
                loading(false);
                updateModelData(data);
                legendModel.data = null;
                resetToLatest();
                updateLayout();
            })
            .on(event.historicFeedError, function(err) {
                console.log('Error getting historic data: ' + err); // TODO: something more useful for the user!
            })
            .on(event.streamingFeedError, function(err) {
                console.log('Error loading data from websocket: ' + err);
            });
    }

    function initialiseHeadMenu(_dataInterface) {
        return menu.head()
            .on(event.dataProductChange, function(product) {
                loading(true);
                updateModelSelectedProduct(product.option);
                updateModelSelectedPeriod(product.option.periods[0]);
                _dataInterface(product.option.periods[0].seconds, product.option);
                render();
            })
            .on(event.dataPeriodChange, function(period) {
                loading(true);
                updateModelSelectedPeriod(period.option);
                _dataInterface(period.option.seconds);
                render();
            })
            .on(event.clearAllPrimaryChartIndicatorsAndSecondaryCharts, function() {
                primaryChartModel.indicators.forEach(deselectOption);
                charts.secondaries.forEach(deselectOption);

                updatePrimaryChartIndicators();
                updateSecondaryCharts();
                render();
            });
    }

    function selectOption(option, options) {
        options.forEach(function(_option) {
            _option.isSelected = false;
        });
        option.isSelected = true;
    }

    function deselectOption(option) { option.isSelected = false; }

    function fetchCoinbaseProducts() {
        coinbaseProducts(insertProductsIntoHeadMenuModel);
    }

    function insertProductsIntoHeadMenuModel(error, bitcoinProducts) {
        if (error) {
            console.log('Error getting Coinbase products: ' + error); // TODO: something more useful for the user!
        } else {
            var defaultPeriods = [hour1, day1];
            var exceptionPeriods = d3.map();
            exceptionPeriods.set('BTC-USD', [minute1, minute5, hour1, day1]);
            var formattedProducts = formatProducts(bitcoinProducts, bitcoinSource, defaultPeriods, exceptionPeriods);
            headMenuModel.products = headMenuModel.products.concat(formattedProducts);
            render();
        }
    }

    function initialiseSelectors() {
        return menu.selectors()
            .on(event.primaryChartSeriesChange, function(series) {
                primaryChartModel.series = series;
                selectOption(series, selectorsModel.seriesSelector.options);
                render();
            })
            .on(event.primaryChartIndicatorChange, onPrimaryIndicatorChange)
            .on(event.secondaryChartChange, onSecondaryChartChange);
    }

    function updatePrimaryChartIndicators() {
        primaryChartModel.indicators =
            selectorsModel.indicatorSelector.indicatorOptions.filter(function(option) {
                return option.isSelected;
            });

        overlayModel.primaryIndicators = primaryChartModel.indicators;
    }

    function updateSecondaryCharts() {
        charts.secondaries =
            selectorsModel.indicatorSelector.secondaryChartOptions.filter(function(option) {
                return option.isSelected;
            });
        // TODO: This doesn't seem to be a concern of menu.
        charts.secondaries.forEach(function(chartOption) {
            chartOption.option.on(event.viewChange, onViewChange);
        });

        overlayModel.secondaryIndicators = charts.secondaries;
        // TODO: Remove .remove! (could a secondary chart group component manage this?).
        containers.secondaries.selectAll('*').remove();
        updateLayout();
    }

    function initialiseOverlay() {
        return menu.overlay()
            .on(event.primaryChartIndicatorChange, onPrimaryIndicatorChange)
            .on(event.secondaryChartChange, onSecondaryChartChange);
    }

    app.run = function() {
        charts.primary = initialisePrimaryChart();
        charts.navbar = initialiseNav();

        var _dataInterface = initialiseDataInterface();
        headMenu = initialiseHeadMenu(_dataInterface);
        navReset = initialiseNavReset();
        selectors = initialiseSelectors();
        overlay = initialiseOverlay();

        updateLayout();
        initialiseResize();

        _dataInterface(generated.periods[0].seconds, generated);
        fetchCoinbaseProducts();
    };

    return app;
}
<|endoftext|>


<commit_before>/* ============================================================
 * Bootstrap: rowlink.js v3.1.0
 * http://jasny.github.io/bootstrap/javascript/#rowlink
 * ============================================================
 * Copyright 2012-2014 Arnold Daniels
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============================================================ */

+function ($) { "use strict";

  var Rowlink = function (element, options) {
    this.$element = $(element)
    this.options = $.extend({}, Rowlink.DEFAULTS, options)

    this.$element.on('click.bs.rowlink', 'td:not(.rowlink-skip)', $.proxy(this.click, this))
  }

  Rowlink.DEFAULTS = {
    target: "a"
  }

  Rowlink.prototype.click = function(e) {
    var target = $(e.currentTarget).closest('tr').find(this.options.target)[0]
    if ($(e.target)[0] === target) return

    e.preventDefault();

    if (target.click) {
      target.click()
    } else if (document.createEvent) {
      var evt = document.createEvent("MouseEvents");
      evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
      target.dispatchEvent(evt);
    }
  }

  // ROWLINK PLUGIN DEFINITION
  // ===========================

  var old = $.fn.rowlink

  $.fn.rowlink = function (options) {
    return this.each(function () {
      var $this = $(this)
      var data = $this.data('rowlink')
      if (!data) $this.data('rowlink', (data = new Rowlink(this, options)))
    })
  }

  $.fn.rowlink.Constructor = Rowlink

  // ROWLINK NO CONFLICT
  // ====================

  $.fn.rowlink.noConflict = function () {
    $.fn.rowlink = old
    return this
  }

  // ROWLINK DATA-API
  // ==================

  $(document).on('click.bs.rowlink.data-api', '[data-link="row"]', function (e) {
    var $this = $(this)
    if ($this.data('rowlink')) return
    $this.rowlink($this.data())
    $(e.target).trigger('click.bs.rowlink')
  })

}(window.jQuery);
<commit_msg>Don't trigger rowlink initiasation when a element within .rowlink-skip is clicked. Fixes #162<commit_after>/* ============================================================
 * Bootstrap: rowlink.js v3.1.1
 * http://jasny.github.io/bootstrap/javascript/#rowlink
 * ============================================================
 * Copyright 2012-2014 Arnold Daniels
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============================================================ */

+function ($) { "use strict";

  var Rowlink = function (element, options) {
    this.$element = $(element)
    this.options = $.extend({}, Rowlink.DEFAULTS, options)

    this.$element.on('click.bs.rowlink', 'td:not(.rowlink-skip)', $.proxy(this.click, this))
  }

  Rowlink.DEFAULTS = {
    target: "a"
  }

  Rowlink.prototype.click = function(e) {
    var target = $(e.currentTarget).closest('tr').find(this.options.target)[0]
    if ($(e.target)[0] === target) return

    e.preventDefault();

    if (target.click) {
      target.click()
    } else if (document.createEvent) {
      var evt = document.createEvent("MouseEvents");
      evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
      target.dispatchEvent(evt);
    }
  }

  // ROWLINK PLUGIN DEFINITION
  // ===========================

  var old = $.fn.rowlink

  $.fn.rowlink = function (options) {
    return this.each(function () {
      var $this = $(this)
      var data = $this.data('rowlink')
      if (!data) $this.data('rowlink', (data = new Rowlink(this, options)))
    })
  }

  $.fn.rowlink.Constructor = Rowlink

  // ROWLINK NO CONFLICT
  // ====================

  $.fn.rowlink.noConflict = function () {
    $.fn.rowlink = old
    return this
  }

  // ROWLINK DATA-API
  // ==================

  $(document).on('click.bs.rowlink.data-api', '[data-link="row"]', function (e) {
    if ($(e.target).closest('.rowlink-skip')) return

    var $this = $(this)
    if ($this.data('rowlink')) return
    $this.rowlink($this.data())
    $(e.target).trigger('click.bs.rowlink')
  })

}(window.jQuery);
<|endoftext|>


<commit_before>var fs = require('fs');
var extend = require('extend');
var util = require('../util');
var mime = require('mime');
var qs = require('querystring');
var Buffer = require('safe-buffer').Buffer;
var PassThrough = require('stream').PassThrough;
var protoMgr = require('../rules/protocols');
var CRLF_RE = /\r\n|\r|\n/g;
var RAW_FILE_RE = /rawfile/;
var HEADERS_SEP_RE = /(\r?\n(?:\r\n|\r|\n)|\r\r\n?)/;
var MAX_HEADERS_SIZE = 256 * 1024;
var TPL_RE = /(?:dust|tpl|jsonp):$/;

function isTplProtocol(protocol) {
  return TPL_RE.test(protocol);
}

function isRawFileProtocol(protocol) {
  return RAW_FILE_RE.test(protocol);
}

function readFiles(files, callback) {
  var file = files.shift();
  var execCallback = function(err, stat) {
    if(!err && stat && stat.isFile()) {
      callback(null, file, stat.size);
    } else if (files.length) {
      readFiles(files, callback);
    } else {
      callback(err || new Error('Not found file ' + file), file);
    }
  };

  !file || typeof file != 'string' ? execCallback() : fs.stat(file, execCallback);
}

function parseRes(str, rawHeaderNames, fromValue) {
  if (!str) {
    return {
      statusCode: 200,
      headers: {}
    };
  }
  var headers = str.split(CRLF_RE);
  var statusLine = headers.shift().trim().split(/\s+/g);
  headers = util.parseHeaders(headers, rawHeaderNames);
  if (fromValue) {
    delete headers['content-encoding'];
  }
  return {
    statusCode: statusLine[1] || 200,
    headers: headers
  };
}

function getRawResByValue(body) {
  var headers;
  if (HEADERS_SEP_RE.test(body)) {
    var crlfStr = RegExp.$1;
    var index = body.indexOf(crlfStr);
    headers = body.substring(0, index);
    body = body.substring(index + crlfStr.length);
  }
  var rawHeaderNames = {};
  var reader = parseRes(headers, rawHeaderNames, true);
  reader.rawHeaderNames = rawHeaderNames;
  reader.body = body;
  return reader;
}

function getRawResByPath(protocol, path, req, size, callback) {
  var isRawFile = isRawFileProtocol(protocol);
  var range = isRawFile ? undefined : util.parseRange(req, req.localFileSize);
  var reader = fs.createReadStream(path, range);
  var rawHeaderNames = reader.rawHeaderNames = rawHeaderNames;
  if (isRawFile) {
    var buffer;
    var done;
    var response = function(err, crlf) {
      if (done) {
        return;
      }
      done = true;
      reader.removeAllListeners('data');
      reader.removeAllListeners('error');
      reader.removeAllListeners('end');
      reader.on('error', util.noop);

      var stream = reader;
      reader = new PassThrough();
      if (err) {
        var stack = util.getErrorStack(err);
        reader.statusCode = 500;
        reader.push(stack);
        reader.push(null);
        size = Buffer.byteLength(stack);
      } else {
        if (crlf) {
          crlf = util.toBuffer(crlf);
          var index = util.indexOfList(buffer, crlf);
          if (index != -1) {
            extend(reader, parseRes(buffer.slice(0, index) + '', rawHeaderNames));
            var headerLen = index + crlf.length;
            size -= headerLen;
            buffer = buffer.slice(headerLen);
          }
        }
        buffer && reader.push(buffer);
        stream.on('error', function(err) {
          reader.emit('error', err);
        });
        stream.pipe(reader);
      }
      callback(reader, null, size);
    };

    reader.on('data', function(data) {
      buffer = buffer ? Buffer.concat([buffer, data]) : data;
      if (HEADERS_SEP_RE.test(buffer + '')) {
        response(null, RegExp.$1);
      } else if (buffer.length > MAX_HEADERS_SIZE) {
        response();
      }
    });
    reader.on('error', response);
    reader.on('end', response);
  } else {
    callback(reader, range, size);
  }
}

function addRangeHeaders(res, range, size) {
  if (!range) {
    return;
  }
  var headers = res.headers;
  headers['content-range'] = 'bytes ' + (range.start + '-' + range.end) + '/' + size;
  headers['accept-ranges'] = 'bytes';
  headers['content-length'] = range.end - range.start + 1;
  res.statusCode = 206;
}

module.exports = function(req, res, next) {
  var options = req.options;
  var config = this.config;
  var protocol = options && options.protocol;
  if (!protoMgr.isFileProxy(protocol)) {
    return next();
  }
  var defaultType = mime.lookup(req.fullUrl.replace(/[?#].*$/, ''), 'text/html');
  var rule = req.rules.rule;
  var addLength = function(reader, length) {
    reader.headers['content-length'] = length;
  };
  if (rule.value) {
    var body = util.removeProtocol(rule.value, true);
    var isRawFile = isRawFileProtocol(protocol);
    var reader = isRawFile ? getRawResByValue(body) : {
      statusCode: 200,
      body: body,
      headers: {
        'content-type': (rule.key ? mime.lookup(rule.key, defaultType) : defaultType) + '; charset=utf-8'
      }
    };

    if (isTplProtocol(protocol)){
      reader.realUrl = rule.matcher;
      render(reader);
    } else {
      if (!isRawFile) {
        var size = Buffer.byteLength(body);
        var range = util.parseRange(req, size);
        if (range) {
          body = Buffer.from(body);
          reader.body = body.slice(range.start, range.end + 1);
          addRangeHeaders(reader, range, size);
        } else {
          addLength(reader, size);
        }
      } else {
        addLength(reader, Buffer.byteLength(body));
      }
      reader = util.wrapResponse(reader);
      reader.realUrl = rule.matcher;
      res.response(reader);
    }
    return;
  }

  readFiles(util.getRuleFiles(rule), function(err, path, size) {
    if (err) {
      if (/^x/.test(protocol)) {
        var fullUrl = /^xs/.test(protocol) ? req.fullUrl.replace(/^http:/, 'https:') : req.fullUrl;
        extend(options, util.parseUrl(fullUrl));
        next();
      } else {
        var notFound = util.wrapResponse({
          statusCode: 404,
          body: 'Not found file ' + path + '',
          headers: {
            'content-type': 'text/html; charset=utf-8'
          }
        });
        notFound.realUrl = path;
        res.response(notFound);
      }
      return;
    }

    var headers = {
      'server': config.name,
      'content-type': mime.lookup(path, defaultType) + '; charset=utf-8'
    };

    if (isTplProtocol(protocol)) {
      var reader = {
        statusCode: 200,
        realUrl: path,
        headers: headers
      };
      fs.readFile(path, {encoding: 'utf8'}, function(err, data) {
        if (err) {
          return util.emitError(req, err);
        }
        reader.body = data;
        render(reader);
      });
    } else {
      req.localFileSize = size;
      getRawResByPath(protocol, path, req, size, function(reader, range, realSize) {
        reader.realUrl = path;
        reader.statusCode = reader.statusCode || 200;
        reader.headers = reader.headers || headers;
        addRangeHeaders(reader, range, size);
        !range && addLength(reader, realSize);
        res.response(reader);
      });
    }
  });

  function render(reader) {
    if (reader.body) {
      var data = qs.parse(util.getQueryString(req.fullUrl));
      if (Object.keys(data).length) {
        reader.body = reader.body.replace(/\$?\{([\w\-$]+)\}/g, function(all, matched) {
          var value = data[matched];
          if (value === undefined) {
            return all;
          }
          return util.getQueryValue(value);
        });
      }
      addLength(reader, Buffer.byteLength(reader.body));
    } else {
      addLength(reader, 0);
    }
    var realUrl = reader.realUrl;
    reader = util.wrapResponse(reader);
    reader.realUrl = realUrl;
    res.response(reader);
  }
};
<commit_msg>refactor: refine code<commit_after>var fs = require('fs');
var extend = require('extend');
var util = require('../util');
var mime = require('mime');
var qs = require('querystring');
var Buffer = require('safe-buffer').Buffer;
var PassThrough = require('stream').PassThrough;
var protoMgr = require('../rules/protocols');
var CRLF_RE = /\r\n|\r|\n/g;
var RAW_FILE_RE = /rawfile/;
var HEADERS_SEP_RE = /(\r?\n(?:\r\n|\r|\n)|\r\r\n?)/;
var MAX_HEADERS_SIZE = 256 * 1024;
var TPL_RE = /(?:dust|tpl|jsonp):$/;

function isTplProtocol(protocol) {
  return TPL_RE.test(protocol);
}

function isRawFileProtocol(protocol) {
  return RAW_FILE_RE.test(protocol);
}

function readFiles(files, callback) {
  var file = files.shift();
  var execCallback = function(err, stat) {
    if(!err && stat && stat.isFile()) {
      callback(null, file, stat.size);
    } else if (files.length) {
      readFiles(files, callback);
    } else {
      callback(err || new Error('Not found file ' + file), file);
    }
  };

  !file || typeof file != 'string' ? execCallback() : fs.stat(file, execCallback);
}

function parseRes(str, rawHeaderNames, fromValue) {
  if (!str) {
    return {
      statusCode: 200,
      headers: {}
    };
  }
  var headers = str.split(CRLF_RE);
  var statusLine = headers.shift().trim().split(/\s+/g);
  headers = util.parseHeaders(headers, rawHeaderNames);
  if (fromValue) {
    delete headers['content-encoding'];
  }
  return {
    statusCode: statusLine[1] || 200,
    headers: headers
  };
}

function getRawResByValue(body) {
  var headers;
  if (HEADERS_SEP_RE.test(body)) {
    var crlfStr = RegExp.$1;
    var index = body.indexOf(crlfStr);
    headers = body.substring(0, index);
    body = body.substring(index + crlfStr.length);
  }
  var rawHeaderNames = {};
  var reader = parseRes(headers, rawHeaderNames, true);
  reader.rawHeaderNames = rawHeaderNames;
  reader.body = body;
  return reader;
}

function getRawResByPath(protocol, path, req, size, callback) {
  var isRawFile = isRawFileProtocol(protocol);
  var range = isRawFile ? undefined : util.parseRange(req, req.localFileSize);
  var reader = fs.createReadStream(path, range);
  var rawHeaderNames = reader.rawHeaderNames = rawHeaderNames;
  if (isRawFile) {
    var buffer;
    var response = function(err, crlf) {
      reader.removeAllListeners();
      reader.on('error', util.noop);
      var stream = reader;
      reader = new PassThrough();
      if (err) {
        var stack = util.getErrorStack(err);
        reader.statusCode = 500;
        reader.push(stack);
        reader.push(null);
        size = Buffer.byteLength(stack);
      } else {
        if (crlf) {
          crlf = util.toBuffer(crlf);
          var index = util.indexOfList(buffer, crlf);
          if (index != -1) {
            extend(reader, parseRes(buffer.slice(0, index) + '', rawHeaderNames));
            var headerLen = index + crlf.length;
            size -= headerLen;
            buffer = buffer.slice(headerLen);
          }
        }
        buffer && reader.push(buffer);
        stream.on('error', function(err) {
          reader.emit('error', err);
        });
        stream.pipe(reader);
      }
      callback(reader, null, size);
    };

    reader.on('data', function(data) {
      buffer = buffer ? Buffer.concat([buffer, data]) : data;
      if (HEADERS_SEP_RE.test(buffer + '')) {
        response(null, RegExp.$1);
      } else if (buffer.length > MAX_HEADERS_SIZE) {
        response();
      }
    });
    reader.on('error', response);
    reader.on('end', response);
  } else {
    callback(reader, range, size);
  }
}

function addRangeHeaders(res, range, size) {
  if (!range) {
    return;
  }
  var headers = res.headers;
  headers['content-range'] = 'bytes ' + (range.start + '-' + range.end) + '/' + size;
  headers['accept-ranges'] = 'bytes';
  headers['content-length'] = range.end - range.start + 1;
  res.statusCode = 206;
}

module.exports = function(req, res, next) {
  var options = req.options;
  var config = this.config;
  var protocol = options && options.protocol;
  if (!protoMgr.isFileProxy(protocol)) {
    return next();
  }
  var defaultType = mime.lookup(req.fullUrl.replace(/[?#].*$/, ''), 'text/html');
  var rule = req.rules.rule;
  var addLength = function(reader, length) {
    reader.headers['content-length'] = length;
  };
  if (rule.value) {
    var body = util.removeProtocol(rule.value, true);
    var isRawFile = isRawFileProtocol(protocol);
    var reader = isRawFile ? getRawResByValue(body) : {
      statusCode: 200,
      body: body,
      headers: {
        'content-type': (rule.key ? mime.lookup(rule.key, defaultType) : defaultType) + '; charset=utf-8'
      }
    };

    if (isTplProtocol(protocol)){
      reader.realUrl = rule.matcher;
      render(reader);
    } else {
      if (!isRawFile) {
        var size = Buffer.byteLength(body);
        var range = util.parseRange(req, size);
        if (range) {
          body = Buffer.from(body);
          reader.body = body.slice(range.start, range.end + 1);
          addRangeHeaders(reader, range, size);
        } else {
          addLength(reader, size);
        }
      } else {
        addLength(reader, Buffer.byteLength(body));
      }
      reader = util.wrapResponse(reader);
      reader.realUrl = rule.matcher;
      res.response(reader);
    }
    return;
  }

  readFiles(util.getRuleFiles(rule), function(err, path, size) {
    if (err) {
      if (/^x/.test(protocol)) {
        var fullUrl = /^xs/.test(protocol) ? req.fullUrl.replace(/^http:/, 'https:') : req.fullUrl;
        extend(options, util.parseUrl(fullUrl));
        next();
      } else {
        var notFound = util.wrapResponse({
          statusCode: 404,
          body: 'Not found file ' + path + '',
          headers: {
            'content-type': 'text/html; charset=utf-8'
          }
        });
        notFound.realUrl = path;
        res.response(notFound);
      }
      return;
    }

    var headers = {
      'server': config.name,
      'content-type': mime.lookup(path, defaultType) + '; charset=utf-8'
    };

    if (isTplProtocol(protocol)) {
      var reader = {
        statusCode: 200,
        realUrl: path,
        headers: headers
      };
      fs.readFile(path, {encoding: 'utf8'}, function(err, data) {
        if (err) {
          return util.emitError(req, err);
        }
        reader.body = data;
        render(reader);
      });
    } else {
      req.localFileSize = size;
      getRawResByPath(protocol, path, req, size, function(reader, range, realSize) {
        reader.realUrl = path;
        reader.statusCode = reader.statusCode || 200;
        reader.headers = reader.headers || headers;
        addRangeHeaders(reader, range, size);
        !range && addLength(reader, realSize);
        res.response(reader);
      });
    }
  });

  function render(reader) {
    if (reader.body) {
      var data = qs.parse(util.getQueryString(req.fullUrl));
      if (Object.keys(data).length) {
        reader.body = reader.body.replace(/\$?\{([\w\-$]+)\}/g, function(all, matched) {
          var value = data[matched];
          if (value === undefined) {
            return all;
          }
          return util.getQueryValue(value);
        });
      }
      addLength(reader, Buffer.byteLength(reader.body));
    } else {
      addLength(reader, 0);
    }
    var realUrl = reader.realUrl;
    reader = util.wrapResponse(reader);
    reader.realUrl = realUrl;
    res.response(reader);
  }
};
<|endoftext|>


<commit_before>/**
 Copyright 2014 Gordon Williams (gw@pur3.co.uk)

 This Source Code is subject to the terms of the Mozilla Public
 License, v2.0. If a copy of the MPL was not distributed with this
 file, You can obtain one at http://mozilla.org/MPL/2.0/.

 ------------------------------------------------------------------
  Settings Page for the Espruino Flasher
 ------------------------------------------------------------------
**/
"use strict";
(function(){

  function init() {
    Espruino.Core.Config.addSection("Flasher", {
      description : "This allows you to update the Espruino Firmware that is on your board",
      sortOrder : 10000,
      getHTML : function(callback) {
        $.get("/data/settings_flasher.html", function(data) {
          callback(data);

          // Set up warning
          var chromeVer = navigator.userAgent.replace(/.*Chrome\/([0-9]*).*/,"$1");
          if (chromeVer < 32) {
            // 32 should work, but if they're going to upgrade let's suggest 33 :)
            var info = "Your Chrome version is "+chromeVer+". Please upgrade it to 33 or newer before trying to flash your Espruino board."
            $(".flash_info").css("color","red").html(info);
          }

          // Start button
          $('.flash_start').click( function() {
            Espruino.Core.App.closePopup();
            Espruino.Core.MenuFlasher.showFlasher();
          });
          // Advanced start

          $('.flash_start_advanced').click( function() {
            var url = $('.flash_url').val();
            if (url!="") {
              Espruino.Core.App.closePopup();
              Espruino.Core.MenuFlasher.showFlasher( url );
            }
          });
        });
      }
    });
  }

  function startFlashing() {

  }

  Espruino.Core.SettingsFlasher = {
    init : init,
  };
}());
<commit_msg>fix #156<commit_after>/**
 Copyright 2014 Gordon Williams (gw@pur3.co.uk)

 This Source Code is subject to the terms of the Mozilla Public
 License, v2.0. If a copy of the MPL was not distributed with this
 file, You can obtain one at http://mozilla.org/MPL/2.0/.

 ------------------------------------------------------------------
  Settings Page for the Espruino Flasher
 ------------------------------------------------------------------
**/
"use strict";
(function(){

  function init() {
    Espruino.Core.Config.addSection("Flasher", {
      description : "This allows you to update the Espruino Firmware that is on your board",
      sortOrder : 10000,
      getHTML : function(callback) {
        $.get("data/settings_flasher.html", function(data) {
          callback(data);

          // Set up warning
          var chromeVer = navigator.userAgent.replace(/.*Chrome\/([0-9]*).*/,"$1");
          if (chromeVer < 32) {
            // 32 should work, but if they're going to upgrade let's suggest 33 :)
            var info = "Your Chrome version is "+chromeVer+". Please upgrade it to 33 or newer before trying to flash your Espruino board."
            $(".flash_info").css("color","red").html(info);
          }

          // Start button
          $('.flash_start').click( function() {
            Espruino.Core.App.closePopup();
            Espruino.Core.MenuFlasher.showFlasher();
          });
          // Advanced start

          $('.flash_start_advanced').click( function() {
            var url = $('.flash_url').val();
            if (url!="") {
              Espruino.Core.App.closePopup();
              Espruino.Core.MenuFlasher.showFlasher( url );
            }
          });
        });
      }
    });
  }

  function startFlashing() {

  }

  Espruino.Core.SettingsFlasher = {
    init : init,
  };
}());
<|endoftext|>


<commit_before>
export default class Graph
{
  constructor()
  {
    this.vertices = [];
    this.edges = [];
    this.numedges = 0;
  }

  addvertex(n)
  {
    this.vertices.push(n);
    this.edges[n] = [];
  }

  addedge(v1, v2, cost)
  {
    this.edges[v1].push({dest:v2, cost});
    this.edges[v2].push({dest:v1, cost});

    this.numedges++;
  }

  // Super basic implementation of Dijkstra's algorithm
  // Directly from this recipe: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#Algorithm
  shortest(start, end)
  {
    let current_node;
    let dist = [0];
    let prev = [];
    let unvisited = [];

    for (let i=0; i<this.vertices.length; i++)
    {
      if (i) dist[i] = Number.MAX_VALUE;
      unvisited[i] = i;
      prev[i] = null;
    }

    while( (current_node = unvisited.shift()) != null )
    {
      for (let t=0; t<this.edges[current_node].length; t++)
      {
        // vertex/node ID
        let neighbour = this.edges[current_node][t].dest;

        // Distance from current_node to neighbour
        let cost = this.edges[current_node][t].cost;

        // Distance thus far on this route (up to current_node) + distance to neighbour
        let tentative_dist = dist[current_node] + cost;

        // Have we found a shorter path?
        if (tentative_dist < dist[neighbour])
        {
          dist[neighbour] = tentative_dist; // New distance to this node
          prev[neighbour] = current_node;   // Update the route
        }

      }
    }

    let c = end, seq =[end];

    // failed for some reason, e.g. impossible point inside nullspace etc
    if (prev[c] == null)
      return [];

    do {
      c = prev[c];
      seq.push(c);
    } while(c != start);

    return seq.reverse();

  }

}
<commit_msg>some brief notes on the Dijkstra implementation/algo<commit_after>
export default class Graph
{
  constructor()
  {
    this.vertices = [];
    this.edges = [];
    this.numedges = 0;
  }

  addvertex(n)
  {
    this.vertices.push(n);
    this.edges[n] = [];
  }

  addedge(v1, v2, cost)
  {
    this.edges[v1].push({dest:v2, cost});
    this.edges[v2].push({dest:v1, cost});

    this.numedges++;
  }

  // Super basic implementation of Dijkstra's algorithm
  // Directly from this recipe: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#Algorithm
  shortest(start, end)
  {
    let current_node;
    let dist = [0];
    let prev = [];
    let unvisited = [];

    for (let i=0; i<this.vertices.length; i++)
    {
      if (i) dist[i] = Number.MAX_VALUE;
      unvisited[i] = i;
      prev[i] = null;
    }

    // 'Visit' each node only once, in turn
    while( (current_node = unvisited.shift()) != null )
    {

      // For each node, 'check' its neighbours.
      // While we only 'visit' each node once, it's this repeated 'check'ing
      // (and occasional recalculating) of neighbours that allows us to find
      // the shortest route through the graph from our start point.
      // In fact, the inherent result of the algo is that we find the shortest
      // path to *every* point in the graph
      for (let t=0; t<this.edges[current_node].length; t++)
      {
        // vertex/node ID
        let neighbour = this.edges[current_node][t].dest;

        // Distance from current_node to neighbour
        let cost = this.edges[current_node][t].cost;

        // Distance thus far on this route (up to current_node) + distance to neighbour
        let tentative_dist = dist[current_node] + cost;

        // Have we found a shorter path?
        if (tentative_dist < dist[neighbour])
        {
          dist[neighbour] = tentative_dist; // New distance to this node
          prev[neighbour] = current_node;   // Update the route
        }

      }
    }

    let c = end, seq =[end];

    // failed for some reason, e.g. impossible point inside nullspace etc
    if (prev[c] == null)
      return [];

    do {
      c = prev[c];
      seq.push(c);
    } while(c != start);

    return seq.reverse();

  }

}
<|endoftext|>