<commit_before>define([
	'jquery',
	'angular',
	'gizmo/superdesk',
	config.guiJs('livedesk', 'authorization')
	], function($, angular, Gizmo, auth){
		function getGizmoUrl(path) {
	        var url = new Gizmo.Url(path);
	        return url.get();
	    }
        function relativeToAbsolute(rel) {
            return window.location.protocol + '//' + window.location.host + rel;
        }
	    var angularHeaders = $.extend({}, auth, { 'Content-Type': 'text/json' });
		var seoconf = angular.module('seoConf',[]);
		seoconf.config(['$interpolateProvider', function($interpolateProvider) {
			$interpolateProvider.startSymbol('{{ ');
			$interpolateProvider.endSymbol(' }}');
		}]);
		seoconf.config(function($httpProvider){
			delete $httpProvider.defaults.headers.common['X-Requested-With'];
			$httpProvider.defaults.headers.get = auth;
			$httpProvider.defaults.headers.post = angularHeaders;
			$httpProvider.defaults.headers.put = angularHeaders;
			$httpProvider.defaults.headers.delete = auth;
		});

		seoconf.factory('seoInterfaceData', ['$http', '$q', function($http, $q){
			return {
				getThemes: function(themeUrl) {
					var deffered = $q.defer();
                    var myData = {};
                    $http({method: 'GET', url: themeUrl}).
                    success(function(data, status, headers, config) {
                        deffered.resolve(data.BlogThemeList);
                    });
                    return deffered.promise;
				},
				getSeoConfigs: function(seoConfigUrl) {
					//get a single seo config's details
					var deffered = $q.defer();
                    $http({method: 'GET', url: seoConfigUrl}).
                    success(function(data, status, headers, config) {
                    	deffered.resolve(data.SeoList);
                    });
                    return deffered.promise;
				},
				getSeoConfig: function(seoConfigUrl) {
					//get all seo configs for this blog
					var deffered = $q.defer();
                    $http({method: 'GET', url: seoConfigUrl}).
                    success(function(data, status, headers, config) {
		                deffered.resolve(data);
                    });
                    return deffered.promise;
				},
				newConfig: function(url, data) {
					var deffered = $q.defer();
                    $http({method: 'POST', url: url, data: data}).
                    success(function(data, status, headers, config) {
                    	$http({method: 'GET', url: data.href}).
	                    success(function(data, status, headers, config) {
	                    	//return the data from the newly created seo config object
	                    	deffered.resolve(data);
	                    });

                    });
                    return deffered.promise;
				},
				editConfig: function(url, data) {
					var deffered = $q.defer();
                    $http({method: 'PUT', url: url, data: data}).
                    success(function(data, status, headers, config) {
                        deffered.resolve(data);
                    });
                    return deffered.promise;
				},
				removeConfig: function(Id) {
					var deffered = $q.defer();
                    $http({method: 'DELETE', url: getGizmoUrl('my/LiveDesk/Seo/' + Id)}).
                    success(function(data, status, headers, config) {
                        deffered.resolve(data);
                    });
                    return deffered.promise;
				}
			}
		}]);

		seoconf.controller('seoInterface', function($scope, $http, seoInterfaceData) {
			var cleanConfig = {
        		Id: 0,
        		SeoTheme: '1',
        		CallbackActive: 'False',
        		RefreshActive: 'False',
        		CallbackURL: '',
        		RefreshInterval: 60,
        		MaxPosts: 10,
        		HtmlURL: '',
      	  		LastSync: 0
        	};
			$scope.boxCounter = 0;
			$scope.updateCheckbox = [];
			$scope.configs = [];
            //initializing some vars
            $scope.addSeoConfig = function() {
            	var config = $scope.completeConfigData($.extend(true,{},cleanConfig));
            	$scope.configs.push(config);
            };
            $scope.init = function(BlogId) {
            	$scope.BlogId = BlogId;
            	//getting the available themes
            	//for new seo configs
	            seoInterfaceData.getThemes(getGizmoUrl('my/LiveDesk/BlogTheme?X-Filter=Name,Id')).then(function(data) {
	            	$scope.availableThemes = data;
	            });
	            seoInterfaceData.getSeoConfigs(getGizmoUrl('my/LiveDesk/Blog/' + $scope.BlogId + '/Seo')).then(function(data) {
	            	//init the configs var

	            	if (data.length == 0) {
	            		//no seo config for this blog yet
	            		var config = $.extend(true,{},cleanConfig);
	            		//add this empty new config to the scope
	            		config = $scope.completeConfigData(config);
	            		//add the rest of the data
	            		$scope.configs.push(config);

	            	} else {
	            		for (var i = 0; i < data.length; i ++) {
	            			seoInterfaceData.getSeoConfig(data[i].href).then(function(data){
	            				//create new config object
	            				var config = {};
	            				config.Id = data.Id;
				            	config.SeoTheme = data.BlogTheme.Id;
				            	config.CallbackActive = data.CallbackActive;
				            	config.CallbackURL = data.CallbackURL;
				            	config.RefreshActive = data.RefreshActive;
				            	config.RefreshInterval = data.RefreshInterval;
				            	config.MaxPosts = data.MaxPosts;
				            	config.HtmlURL = data.HtmlURL;
				            	config.LastSync = data.LastSync;

				            	config = $scope.completeConfigData(config);
				            	//add the rest of the data
	            				$scope.configs.push(config);
	            			});
	            		}
	            	}
	            });
            };
            $scope.showRemove = function(i) {
            	return i ? true : false;
            };
            $scope.showPartition = function(i) {
            	return i ? false : true;
            };
            $scope.toggleExtraInfo = function(i) {
            	$scope.configs[i].showExtra = $scope.configs[i].showExtra === false ? true : false;
            	if ( $scope.configs[i].showExtra ) {
					var extraInfoText = _('Show less');
            	} else {
            		var extraInfoText = _('Show more');
            	}
            	$scope.configs[i].extraInfoText = extraInfoText.toString();
            };
            $scope.completeConfigData = function(config) {
            	config.showExtra = false;
            	var extraInfoText = _('Show more');
            	config.extraInfoText = extraInfoText.toString();
            	config.HtmlWaitSwitch = false;
            	//on-off switches for data that may or may not be present
            	config.HtmlURLSwitch = false;
            	config.CallbackStatusSwitch = false;
            	//vars to keep the text info
            	config.HtmlURLText = '';
            	config.CallbackStatusText = '';
            	//general info on the html created
            	if (config.Id > 0 && config.HtmlURL && config.LastSync) {
                    config.HtmlWaitSwitch = false;
            		config.HtmlURLSwitch = true;
            		config.HtmlURL = relativeToAbsolute(config.HtmlURL);
            		config.HtmlURLText =  _('SEO html');
            		//add timeinfo to text if it exists
            		if ( config.Id > 0 && config.LastSync ) {
            			var LastSync = new Date( config.LastSync );
            			config.HtmlURLText += _(' generated on ');
            			config.HtmlURLText += LastSync.format('yyyy-mm-dd HH:MM:ss');
            		}
            	}
            	config.HtmlURLText = config.HtmlURLText.toString();
            	//info on callback status if it exists
            	if (config.Id > 0 && config.CallbackStatus ) {
            		config.CallbackStatusSwitch = true;
            		config.CallbackStatusText = _('Result: ');
            		config.CallbackStatusText += config.CallbackStatus;
            	}
            	return config;
            };

            $scope.handleHtmlLink = function(seoUrl, i) {
                $scope.configs[i].HtmlURLSwitch = false;
                $scope.configs[i].CallbackStatusSwitch = false;
                $scope.configs[i].HtmlWaitSwitch = true;
                var waitText = _('Please wait while the HTML is generating...');
                $scope.configs[i].HtmlWaitText = waitText.toString();
                if ($scope.configs[i].RefreshActive == "False") {
                    $scope.configs[i].HtmlWaitSwitch = false;
                    return;
                }

                if ($scope.configs[i].htmlInterval) {
                    clearInterval($scope.configs[i].htmlInterval);
                }

                var counter = 0;
                $scope.configs[i].htmlInterval = setInterval(function() {
                    $http({method: 'GET', url: seoUrl, headers: {'X-Format-DateTime': "yyyy-MM-ddTHH:mm:ss'Z'"}}).
                    success(function(data, status, headers, config) {
                        //return the data from the newly created seo config object
                        var changedOn = Date.parse(data.ChangedOn);
                        counter ++;
                        if (counter == 20) {
                            clearInterval($scope.configs[i].htmlInterval);
                            var text = _('The HTML generation process is taking too long, please refresh the page at a later time');
                            $scope.configs[i].HtmlWaitText = text.toString();
                        }
                        if (data.CallbackStatus) {
                            clearInterval($scope.configs[i].htmlInterval);
                            $scope.configs[i].HtmlURLSwitch = false;
                            $scope.configs[i].HtmlWaitSwitch = false;
                            $scope.configs[i].CallbackStatusText = _('Result: ');
                            $scope.configs[i].CallbackStatusText += data.CallbackStatus;
                            $scope.configs[i].CallbackStatusSwitch = true;
                        }
                        if (data.LastSync) {
                            var LastSyncOn = Date.parse(data.LastSync);
                            if ( LastSyncOn >= changedOn) {
                                clearInterval($scope.configs[i].htmlInterval);
                                $scope.configs[i].HtmlURL = relativeToAbsolute(data.HtmlURL);
                                var urlText = _('SEO html');
                                $scope.configs[i].HtmlURLText = urlText.toString();
                                $scope.configs[i].HtmlURLSwitch = true;
                                $scope.configs[i].HtmlWaitSwitch = false;
                            }
                        }
                    });
                }, 10000);
            };
            //loop through all configs and save them one by one
            $scope.removeConfig = function(index) {
            	if ($scope.configs[index].Id) {
					seoInterfaceData.removeConfig($scope.configs[index].Id).then(function(){
	            		$scope.configs.splice(index, 1);
	            	});
            	} else {
            		$scope.configs.splice(index, 1);
            	}
            };
            $scope.isLast = function(i) {
            	return $scope.configs.length - 1 == i;
            }
            $scope.saveAllConfigs = function() {
            	for (var i = 0; i < $scope.configs.length; i ++) {
            		$scope.save($scope.configs[i], i);
            	}
            };
            $scope.save = function(config, index) {
            	var data = {
            		BlogTheme: config.SeoTheme.toString(),
	            	CallbackActive: config.CallbackActive,
	            	CallbackURL: config.CallbackURL,
	            	RefreshActive: config.RefreshActive,
	            	RefreshInterval: config.RefreshInterval.toString(),
	            	MaxPosts: config.MaxPosts.toString()
            	}
            	if ( config.Id == 0 ) {
            		//new request
            		data.Blog = $scope.BlogId.toString();
            		seoInterfaceData.newConfig(getGizmoUrl('my/LiveDesk/Seo'), data).then(function(data) {
            			//set the seo object scope id to the newly created one
            			config.Id = data.Id;
                        $scope.handleHtmlLink(getGizmoUrl('my/LiveDesk/Seo/' + config.Id), index);
            		});
            	} else {
					seoInterfaceData.editConfig(getGizmoUrl('my/LiveDesk/Seo/' + config.Id), data).then(function(data) {
            			//stuff do to after seo config edit
                        $scope.handleHtmlLink(getGizmoUrl('my/LiveDesk/Seo/' + config.Id), index);
            		});
            	}
            };
            $scope.toggleSave = function(index) {
            	$scope.save($scope.configs[index], index);
            }
        });

		seoconf.directive('seoToggler', function() {
			return {
				restrict: 'E',
				scope: {
					toggle: "=",
					action: "&"
				},
				template: '

',
				link: function(scope, element, attrs) {
					if ( scope.toggle == attrs['trueValue'] ) {
						element.find('.sf-toggle-custom').addClass('sf-checked');
					}
					element.find('.sf-toggle-custom').on('click', function(){
						$(this).toggleClass('sf-checked');
						if ( $(this).hasClass('sf-checked') ) {
							scope.toggle = attrs['trueValue'];
						} else {
							scope.toggle = attrs['falseValue'];
						}
                        scope.$apply();
                        scope.action(attrs['index']);
					})
				}
			}
		})

        return seoconf;
	});<commit_msg>LB-1892: Default from db in any<commit_after>define([
	'jquery',
	'angular',
	'gizmo/superdesk',
	config.guiJs('livedesk', 'authorization')
	], function($, angular, Gizmo, auth){
		function getGizmoUrl(path) {
	        var url = new Gizmo.Url(path);
	        return url.get();
	    }
        function relativeToAbsolute(rel) {
            return window.location.protocol + '//' + window.location.host + rel;
        }
	    var angularHeaders = $.extend({}, auth, { 'Content-Type': 'text/json' });
		var seoconf = angular.module('seoConf',[]);
		seoconf.config(['$interpolateProvider', function($interpolateProvider) {
			$interpolateProvider.startSymbol('{{ ');
			$interpolateProvider.endSymbol(' }}');
		}]);
		seoconf.config(function($httpProvider){
			delete $httpProvider.defaults.headers.common['X-Requested-With'];
			$httpProvider.defaults.headers.get = auth;
			$httpProvider.defaults.headers.post = angularHeaders;
			$httpProvider.defaults.headers.put = angularHeaders;
			$httpProvider.defaults.headers.delete = auth;
		});

		seoconf.factory('seoInterfaceData', ['$http', '$q', function($http, $q){
			return {
				getSeoConfigDefaults: function() {
					var deffered = $q.defer();
                    var myData = {};
                    var configUrl = getGizmoUrl('GeneralSetting?group=seo&X-Filter=Value,Key')
                    $http({method: 'GET', url: configUrl}).
                    success(function(data, status, headers, config) {
                        deffered.resolve(data.GeneralSettingList);
                    });
                    return deffered.promise;
				},
				getThemes: function(themeUrl) {
					var deffered = $q.defer();
                    var myData = {};
                    $http({method: 'GET', url: themeUrl}).
                    success(function(data, status, headers, config) {
                        deffered.resolve(data.BlogThemeList);
                    });
                    return deffered.promise;
				},
				getSeoConfigs: function(seoConfigUrl) {
					//get a single seo config's details
					var deffered = $q.defer();
                    $http({method: 'GET', url: seoConfigUrl}).
                    success(function(data, status, headers, config) {
                    	deffered.resolve(data.SeoList);
                    });
                    return deffered.promise;
				},
				getSeoConfig: function(seoConfigUrl) {
					//get all seo configs for this blog
					var deffered = $q.defer();
                    $http({method: 'GET', url: seoConfigUrl}).
                    success(function(data, status, headers, config) {
		                deffered.resolve(data);
                    });
                    return deffered.promise;
				},
				newConfig: function(url, data) {
					var deffered = $q.defer();
                    $http({method: 'POST', url: url, data: data}).
                    success(function(data, status, headers, config) {
                    	$http({method: 'GET', url: data.href}).
	                    success(function(data, status, headers, config) {
	                    	//return the data from the newly created seo config object
	                    	deffered.resolve(data);
	                    });

                    });
                    return deffered.promise;
				},
				editConfig: function(url, data) {
					var deffered = $q.defer();
                    $http({method: 'PUT', url: url, data: data}).
                    success(function(data, status, headers, config) {
                        deffered.resolve(data);
                    });
                    return deffered.promise;
				},
				removeConfig: function(Id) {
					var deffered = $q.defer();
                    $http({method: 'DELETE', url: getGizmoUrl('my/LiveDesk/Seo/' + Id)}).
                    success(function(data, status, headers, config) {
                        deffered.resolve(data);
                    });
                    return deffered.promise;
				}
			}
		}]);

		seoconf.controller('seoInterface', function($scope, $http, seoInterfaceData) {
			$scope.cleanConfig = {
        		Id: 0,
        		SeoTheme: '1',
        		CallbackActive: 'False',
        		RefreshActive: 'False',
        		CallbackURL: '',
        		RefreshInterval: 60,
        		MaxPosts: 10,
        		HtmlURL: '',
      	  		LastSync: 0
        	};
        	$scope.defaultConfig = {};
			$scope.boxCounter = 0;
			$scope.updateCheckbox = [];
			$scope.configs = [];
            //initializing some vars
            $scope.addSeoConfig = function() {
            	var config = $scope.completeConfigData($.extend(true,{},$scope.cleanConfig));
            	$scope.configs.push(config);
            };
            $scope.getThemes = function() {
            	seoInterfaceData.getThemes(getGizmoUrl('my/LiveDesk/BlogTheme?X-Filter=Name,Id')).then(function(data) {
	            	$scope.availableThemes = data;
	            });
            };
            $scope.getSeoConfigs = function() {
            	seoInterfaceData.getSeoConfigs(getGizmoUrl('my/LiveDesk/Blog/' + $scope.BlogId + '/Seo')).then(function(data) {
	            	//init the configs var

	            	if (data.length == 0) {
	            		//no seo config for this blog yet
	            		var config = $.extend(true,{},$scope.cleanConfig);
	            		//add this empty new config to the scope
	            		config = $scope.completeConfigData(config);
	            		//add the rest of the data
	            		$scope.configs.push(config);

	            	} else {
	            		for (var i = 0; i < data.length; i ++) {
	            			seoInterfaceData.getSeoConfig(data[i].href).then(function(data){
	            				//create new config object
	            				var config = {};
	            				config.Id = data.Id;
				            	config.SeoTheme = data.BlogTheme.Id;
				            	config.CallbackActive = data.CallbackActive;
				            	config.CallbackURL = data.CallbackURL;
				            	config.RefreshActive = data.RefreshActive;
				            	config.RefreshInterval = data.RefreshInterval;
				            	config.MaxPosts = data.MaxPosts;
				            	config.HtmlURL = data.HtmlURL;
				            	config.LastSync = data.LastSync;

				            	config = $scope.completeConfigData(config);
				            	//add the rest of the data
	            				$scope.configs.push(config);
	            			});
	            		}
	            	}
	            });
            };
            $scope.init = function(BlogId) {
            	$scope.BlogId = BlogId;
            	//getting the available themes
            	//for new seo configs
            	$scope.getThemes();
            	seoInterfaceData.getSeoConfigDefaults().then(function(data){
            		//create the default config object from db settings
            		for ( var i = 0; i < data.length; i++ ) {
            			$scope.defaultConfig[data[i].Key] = data[i].Value;
            		}
            		$scope.cleanConfig = $.extend(true, $scope.cleanConfig, $scope.defaultConfig);
            		$scope.getSeoConfigs();
            	});
            };
            $scope.showRemove = function(i) {
            	return i ? true : false;
            };
            $scope.showPartition = function(i) {
            	return i ? false : true;
            };
            $scope.toggleExtraInfo = function(i) {
            	$scope.configs[i].showExtra = $scope.configs[i].showExtra === false ? true : false;
            	if ( $scope.configs[i].showExtra ) {
					var extraInfoText = _('Show less');
            	} else {
            		var extraInfoText = _('Show more');
            	}
            	$scope.configs[i].extraInfoText = extraInfoText.toString();
            };
            $scope.completeConfigData = function(config) {
            	config.showExtra = false;
            	var extraInfoText = _('Show more');
            	config.extraInfoText = extraInfoText.toString();
            	config.HtmlWaitSwitch = false;
            	//on-off switches for data that may or may not be present
            	config.HtmlURLSwitch = false;
            	config.CallbackStatusSwitch = false;
            	//vars to keep the text info
            	config.HtmlURLText = '';
            	config.CallbackStatusText = '';
            	//general info on the html created
            	if (config.Id > 0 && config.HtmlURL && config.LastSync) {
                    config.HtmlWaitSwitch = false;
            		config.HtmlURLSwitch = true;
            		config.HtmlURL = relativeToAbsolute(config.HtmlURL);
            		config.HtmlURLText =  _('SEO html');
            		//add timeinfo to text if it exists
            		if ( config.Id > 0 && config.LastSync ) {
            			var LastSync = new Date( config.LastSync );
            			config.HtmlURLText += _(' generated on ');
            			config.HtmlURLText += LastSync.format('yyyy-mm-dd HH:MM:ss');
            		}
            	}
            	config.HtmlURLText = config.HtmlURLText.toString();
            	//info on callback status if it exists
            	if (config.Id > 0 && config.CallbackStatus ) {
            		config.CallbackStatusSwitch = true;
            		config.CallbackStatusText = _('Result: ');
            		config.CallbackStatusText += config.CallbackStatus;
            	}
            	return config;
            };

            $scope.handleHtmlLink = function(seoUrl, i) {
                $scope.configs[i].HtmlURLSwitch = false;
                $scope.configs[i].CallbackStatusSwitch = false;
                $scope.configs[i].HtmlWaitSwitch = true;
                var waitText = _('Please wait while the HTML is generating...');
                $scope.configs[i].HtmlWaitText = waitText.toString();
                if ($scope.configs[i].RefreshActive == "False") {
                    $scope.configs[i].HtmlWaitSwitch = false;
                    return;
                }

                if ($scope.configs[i].htmlInterval) {
                    clearInterval($scope.configs[i].htmlInterval);
                }

                var counter = 0;
                $scope.configs[i].htmlInterval = setInterval(function() {
                    $http({method: 'GET', url: seoUrl, headers: {'X-Format-DateTime': "yyyy-MM-ddTHH:mm:ss'Z'"}}).
                    success(function(data, status, headers, config) {
                        //return the data from the newly created seo config object
                        var changedOn = Date.parse(data.ChangedOn);
                        counter ++;
                        if (counter == 20) {
                            clearInterval($scope.configs[i].htmlInterval);
                            var text = _('The HTML generation process is taking too long, please refresh the page at a later time');
                            $scope.configs[i].HtmlWaitText = text.toString();
                        }
                        if (data.CallbackStatus) {
                            clearInterval($scope.configs[i].htmlInterval);
                            $scope.configs[i].HtmlURLSwitch = false;
                            $scope.configs[i].HtmlWaitSwitch = false;
                            $scope.configs[i].CallbackStatusText = _('Result: ');
                            $scope.configs[i].CallbackStatusText += data.CallbackStatus;
                            $scope.configs[i].CallbackStatusSwitch = true;
                        }
                        if (data.LastSync) {
                            var LastSyncOn = Date.parse(data.LastSync);
                            if ( LastSyncOn >= changedOn) {
                                clearInterval($scope.configs[i].htmlInterval);
                                $scope.configs[i].HtmlURL = relativeToAbsolute(data.HtmlURL);
                                var urlText = _('SEO html');
                                $scope.configs[i].HtmlURLText = urlText.toString();
                                $scope.configs[i].HtmlURLSwitch = true;
                                $scope.configs[i].HtmlWaitSwitch = false;
                            }
                        }
                    });
                }, 10000);
            };
            //loop through all configs and save them one by one
            $scope.removeConfig = function(index) {
            	if ($scope.configs[index].Id) {
					seoInterfaceData.removeConfig($scope.configs[index].Id).then(function(){
	            		$scope.configs.splice(index, 1);
	            	});
            	} else {
            		$scope.configs.splice(index, 1);
            	}
            };
            $scope.isLast = function(i) {
            	return $scope.configs.length - 1 == i;
            }
            $scope.saveAllConfigs = function() {
            	for (var i = 0; i < $scope.configs.length; i ++) {
            		$scope.save($scope.configs[i], i);
            	}
            };
            $scope.save = function(config, index) {
            	var data = {
            		BlogTheme: config.SeoTheme.toString(),
	            	CallbackActive: config.CallbackActive,
	            	CallbackURL: config.CallbackURL,
	            	RefreshActive: config.RefreshActive,
	            	RefreshInterval: config.RefreshInterval.toString(),
	            	MaxPosts: config.MaxPosts.toString()
            	}
            	if ( config.Id == 0 ) {
            		//new request
            		data.Blog = $scope.BlogId.toString();
            		seoInterfaceData.newConfig(getGizmoUrl('my/LiveDesk/Seo'), data).then(function(data) {
            			//set the seo object scope id to the newly created one
            			config.Id = data.Id;
                        $scope.handleHtmlLink(getGizmoUrl('my/LiveDesk/Seo/' + config.Id), index);
            		});
            	} else {
					seoInterfaceData.editConfig(getGizmoUrl('my/LiveDesk/Seo/' + config.Id), data).then(function(data) {
            			//stuff do to after seo config edit
                        $scope.handleHtmlLink(getGizmoUrl('my/LiveDesk/Seo/' + config.Id), index);
            		});
            	}
            };
            $scope.toggleSave = function(index) {
            	$scope.save($scope.configs[index], index);
            }
        });

		seoconf.directive('seoToggler', function() {
			return {
				restrict: 'E',
				scope: {
					toggle: "=",
					action: "&"
				},
				template: '

',
				link: function(scope, element, attrs) {
					if ( scope.toggle == attrs['trueValue'] ) {
						element.find('.sf-toggle-custom').addClass('sf-checked');
					}
					element.find('.sf-toggle-custom').on('click', function(){
						$(this).toggleClass('sf-checked');
						if ( $(this).hasClass('sf-checked') ) {
							scope.toggle = attrs['trueValue'];
						} else {
							scope.toggle = attrs['falseValue'];
						}
                        scope.$apply();
                        scope.action(attrs['index']);
					})
				}
			}
		})

        return seoconf;
	});<|endoftext|>


<commit_before>/*globals qq */
qq.s3 = qq.s3 || {};

qq.s3.util = qq.s3.util || (function() {
    "use strict";

    return {
        AWS_PARAM_PREFIX: "x-amz-meta-",

        SESSION_TOKEN_PARAM_NAME: "x-amz-security-token",

        REDUCED_REDUNDANCY_PARAM_NAME: "x-amz-storage-class",
        REDUCED_REDUNDANCY_PARAM_VALUE: "REDUCED_REDUNDANCY",

        SERVER_SIDE_ENCRYPTION_PARAM_NAME: "x-amz-server-side-encryption",
        SERVER_SIDE_ENCRYPTION_PARAM_VALUE: "AES256",

        /**
         * This allows for the region to be specified in the bucket's endpoint URL, or not.
         *
         * Examples of some valid endpoints are:
         *     http://foo.s3.amazonaws.com
         *     https://foo.s3.amazonaws.com
         *     http://foo.s3-ap-northeast-1.amazonaws.com
         *     foo.s3.amazonaws.com
         *     http://foo.bar.com
         *     http://s3.amazonaws.com/foo.bar.com
         * ...etc
         *
         * @param endpoint The bucket's URL.
         * @returns {String || undefined} The bucket name, or undefined if the URL cannot be parsed.
         */
        getBucket: function(endpoint) {
            var patterns = [
                    //bucket in domain
                    /^(?:https?:\/\/)?([a-z0-9.\-_]+)\.s3(?:-[a-z0-9\-]+)?\.amazonaws\.com/i,
                    //bucket in path
                    /^(?:https?:\/\/)?s3(?:-[a-z0-9\-]+)?\.amazonaws\.com\/([a-z0-9.\-_]+)/i,
                    //custom domain
                    /^(?:https?:\/\/)?([a-z0-9.\-_]+)/i
                ],
                bucket;

            qq.each(patterns, function(idx, pattern) {
                var match = pattern.exec(endpoint);

                if (match) {
                    bucket = match[1];
                    return false;
                }
            });

            return bucket;
        },

        /** Create Prefixed request headers which are appropriate for S3.
         *
         * If the request header is appropriate for S3 (e.g. Cache-Control) then pass
         * it along without a metadata prefix. For all other request header parameter names,
         * apply qq.s3.util.AWS_PARAM_PREFIX before the name.
         *
         * @param name Name of the Request Header parameter to construct a (possibly) prefixed name.
         * @returns {String} A valid Request Header parameter name.
         */
        _getPrefixedParamName: function (name) {
            switch (name)
            {
                //
                // Valid request headers (not sent by fine-uploader) which should be returned as-is (case-sensitive)
                // see: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html
                //
                case "Cache-Control":
                case "Content-Disposition":
                case "Content-Encoding":
                case "Content-MD5": // CAW: Content-MD5 might not be appropriate from user-land
                case "x-amz-server-side-encryption-customer-algorithm":
                case "x-amz-server-side-encryption-customer-key":
                case "x-amz-server-side-encryption-customer-key-MD5":
                    return name;
                default:
                    return qq.s3.util.AWS_PARAM_PREFIX + name;
            }
        },

        /**
         * Create a policy document to be signed and sent along with the S3 upload request.
         *
         * @param spec Object with properties use to construct the policy document.
         * @returns {Object} Policy doc.
         */
        getPolicy: function(spec) {
            var policy = {},
                conditions = [],
                bucket = qq.s3.util.getBucket(spec.endpoint),
                key = spec.key,
                acl = spec.acl,
                type = spec.type,
                expirationDate = new Date(),
                expectedStatus = spec.expectedStatus,
                sessionToken = spec.sessionToken,
                params = spec.params,
                successRedirectUrl = qq.s3.util.getSuccessRedirectAbsoluteUrl(spec.successRedirectUrl),
                minFileSize = spec.minFileSize,
                maxFileSize = spec.maxFileSize,
                reducedRedundancy = spec.reducedRedundancy,
                serverSideEncryption = spec.serverSideEncryption;

            policy.expiration = qq.s3.util.getPolicyExpirationDate(expirationDate);

            conditions.push({acl: acl});
            conditions.push({bucket: bucket});

            if (type) {
                conditions.push({"Content-Type": type});
            }

            if (expectedStatus) {
                conditions.push({success_action_status: expectedStatus.toString()});
            }

            if (successRedirectUrl) {
                conditions.push({success_action_redirect: successRedirectUrl});
            }

            if (reducedRedundancy) {
                conditions.push({});
                conditions[conditions.length - 1][qq.s3.util.REDUCED_REDUNDANCY_PARAM_NAME] = qq.s3.util.REDUCED_REDUNDANCY_PARAM_VALUE;
            }

            if (sessionToken) {
                conditions.push({});
                conditions[conditions.length - 1][qq.s3.util.SESSION_TOKEN_PARAM_NAME] = sessionToken;
            }

            if (serverSideEncryption) {
                conditions.push({});
                conditions[conditions.length - 1][qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_NAME] = qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_VALUE;
            }

            conditions.push({key: key});

            // user metadata
            qq.each(params, function(name, val) {
                var awsParamName = qq.s3.util._getPrefixedParamName(name),
                    param = {};

                param[awsParamName] = encodeURIComponent(val);
                conditions.push(param);
            });

            policy.conditions = conditions;

            qq.s3.util.enforceSizeLimits(policy, minFileSize, maxFileSize);

            return policy;
        },

        /**
         * Update a previously constructed policy document with updated credentials.  Currently, this only requires we
         * update the session token.  This is only relevant if requests are being signed client-side.
         *
         * @param policy Live policy document
         * @param newSessionToken Updated session token.
         */
        refreshPolicyCredentials: function(policy, newSessionToken) {
            var sessionTokenFound = false;

            qq.each(policy.conditions, function(oldCondIdx, oldCondObj) {
                qq.each(oldCondObj, function(oldCondName, oldCondVal) {
                    if (oldCondName === qq.s3.util.SESSION_TOKEN_PARAM_NAME) {
                        oldCondObj[oldCondName] = newSessionToken;
                        sessionTokenFound = true;
                    }
                });
            });

            if (!sessionTokenFound) {
                policy.conditions.push({});
                policy.conditions[policy.conditions.length - 1][qq.s3.util.SESSION_TOKEN_PARAM_NAME] = newSessionToken;
            }
        },

        /**
         * Generates all parameters to be passed along with the S3 upload request.  This includes invoking a callback
         * that is expected to asynchronously retrieve a signature for the policy document.  Note that the server
         * signing the request should reject a "tainted" policy document that includes unexpected values, since it is
         * still possible for a malicious user to tamper with these values during policy document generation,
         * before it is sent to the server for signing.
         *
         * @param spec Object with properties: `params`, `type`, `key`, `accessKey`, `acl`, `expectedStatus`, `successRedirectUrl`,
         * `reducedRedundancy`, serverSideEncryption, and `log()`, along with any options associated with `qq.s3.util.getPolicy()`.
         * @returns {qq.Promise} Promise that will be fulfilled once all parameters have been determined.
         */
        generateAwsParams: function(spec, signPolicyCallback) {
            var awsParams = {},
                customParams = spec.params,
                promise = new qq.Promise(),
                policyJson = qq.s3.util.getPolicy(spec),
                sessionToken = spec.sessionToken,
                type = spec.type,
                key = spec.key,
                accessKey = spec.accessKey,
                acl = spec.acl,
                expectedStatus = spec.expectedStatus,
                successRedirectUrl = qq.s3.util.getSuccessRedirectAbsoluteUrl(spec.successRedirectUrl),
                reducedRedundancy = spec.reducedRedundancy,
                serverSideEncryption = spec.serverSideEncryption,
                log = spec.log;

            awsParams.key = key;
            awsParams.AWSAccessKeyId = accessKey;

            if (type) {
                awsParams["Content-Type"] = type;
            }

            if (expectedStatus) {
                awsParams.success_action_status = expectedStatus;
            }

            if (successRedirectUrl) {
                awsParams.success_action_redirect = successRedirectUrl;
            }

            if (reducedRedundancy) {
                awsParams[qq.s3.util.REDUCED_REDUNDANCY_PARAM_NAME] = qq.s3.util.REDUCED_REDUNDANCY_PARAM_VALUE;
            }

            if (serverSideEncryption) {
                awsParams[qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_NAME] = qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_VALUE;
            }

            if (sessionToken) {
                awsParams[qq.s3.util.SESSION_TOKEN_PARAM_NAME] = sessionToken;
            }

            awsParams.acl = acl;

            // Custom (user-supplied) params must be prefixed with the value of `qq.s3.util.AWS_PARAM_PREFIX`.
            // Params such as Cache-Control or Content-Disposition will not be prefixed.
            // All param values will be URI encoded as well.
            qq.each(customParams, function(name, val) {
                var awsParamName = qq.s3.util._getPrefixedParamName(name);
                awsParams[awsParamName] = encodeURIComponent(val);
            });

            // Invoke a promissory callback that should provide us with a base64-encoded policy doc and an
            // HMAC signature for the policy doc.
            signPolicyCallback(policyJson).then(
                function(policyAndSignature, updatedAccessKey, updatedSessionToken) {
                    awsParams.policy = policyAndSignature.policy;
                    awsParams.signature = policyAndSignature.signature;

                    if (updatedAccessKey) {
                        awsParams.AWSAccessKeyId = updatedAccessKey;
                    }
                    if (updatedSessionToken) {
                        awsParams[qq.s3.util.SESSION_TOKEN_PARAM_NAME] = updatedSessionToken;
                    }

                    promise.success(awsParams);
                },
                function(errorMessage) {
                    errorMessage = errorMessage || "Can't continue further with request to S3 as we did not receive " +
                                                   "a valid signature and policy from the server.";

                    log("Policy signing failed.  " + errorMessage, "error");
                    promise.failure(errorMessage);
                }
            );

            return promise;
        },

        /**
         * Add a condition to an existing S3 upload request policy document used to ensure AWS enforces any size
         * restrictions placed on files server-side.  This is important to do, in case users mess with the client-side
         * checks already in place.
         *
         * @param policy Policy document as an `Object`, with a `conditions` property already attached
         * @param minSize Minimum acceptable size, in bytes
         * @param maxSize Maximum acceptable size, in bytes (0 = unlimited)
         */
        enforceSizeLimits: function(policy, minSize, maxSize) {
            var adjustedMinSize = minSize < 0 ? 0 : minSize,
                // Adjust a maxSize of 0 to the largest possible integer, since we must specify a high and a low in the request
                adjustedMaxSize = maxSize <= 0 ? 9007199254740992 : maxSize;

            if (minSize > 0 || maxSize > 0) {
                policy.conditions.push(["content-length-range", adjustedMinSize.toString(), adjustedMaxSize.toString()]);
            }
        },

        getPolicyExpirationDate: function(date) {
            /*jshint -W014 */
            // Is this going to be a problem if we encounter this moments before 2 AM just before daylight savings time ends?
            date.setMinutes(date.getMinutes() + 5);

            if (Date.prototype.toISOString) {
                return date.toISOString();
            }
            else {
                var pad = function(number) {
                    var r = String(number);

                    if ( r.length === 1 ) {
                        r = "0" + r;
                    }

                    return r;
                };

                return date.getUTCFullYear()
                        + "-" + pad( date.getUTCMonth() + 1 )
                        + "-" + pad( date.getUTCDate() )
                        + "T" + pad( date.getUTCHours() )
                        + ":" + pad( date.getUTCMinutes() )
                        + ":" + pad( date.getUTCSeconds() )
                        + "." + String( (date.getUTCMilliseconds()/1000).toFixed(3) ).slice( 2, 5 )
                        + "Z";
            }
        },

        /**
         * Looks at a response from S3 contained in an iframe and parses the query string in an attempt to identify
         * the associated resource.
         *
         * @param iframe Iframe containing response
         * @returns {{bucket: *, key: *, etag: *}}
         */
        parseIframeResponse: function(iframe) {
            var doc = iframe.contentDocument || iframe.contentWindow.document,
                queryString = doc.location.search,
                match = /bucket=(.+)&key=(.+)&etag=(.+)/.exec(queryString);

            if (match) {
                return {
                    bucket: match[1],
                    key: match[2],
                    etag: match[3].replace(/%22/g, "")
                };
            }
        },

        /**
         * @param successRedirectUrl Relative or absolute location of success redirect page
         * @returns {*|string} undefined if the parameter is undefined, otherwise the absolute location of the success redirect page
         */
        getSuccessRedirectAbsoluteUrl: function(successRedirectUrl) {
            if (successRedirectUrl) {
                var targetAnchorContainer = document.createElement("div"),
                    targetAnchor;

                if (qq.ie7()) {
                    // Note that we must make use of `innerHTML` for IE7 only instead of simply creating an anchor via
                    // `document.createElement('a')` and setting the `href` attribute.  The latter approach does not allow us to
                    // obtain an absolute URL in IE7 if the `endpoint` is a relative URL.
                    targetAnchorContainer.innerHTML = "";
                    targetAnchor = targetAnchorContainer.firstChild;
                    return targetAnchor.href;
                }
                else {
                    // IE8 and IE9 do not seem to derive an absolute URL from a relative URL using the `innerHTML`
                    // approach above, so we'll just create an anchor this way and set it's `href` attribute.
                    // Due to yet another quirk in IE8 and IE9, we have to set the `href` equal to itself
                    // in order to ensure relative URLs will be properly parsed.
                    targetAnchor = document.createElement("a");
                    targetAnchor.href = successRedirectUrl;
                    targetAnchor.href = targetAnchor.href;
                    return targetAnchor.href;
                }
            }
        },

        // AWS employs a strict interpretation of [RFC 3986](http://tools.ietf.org/html/rfc3986#page-12).
        // So, we must ensure all reserved characters listed in the spec are percent-encoded,
        // and spaces are replaced with "+".
        encodeQueryStringParam: function(param) {
            var percentEncoded = encodeURIComponent(param);

            // %-encode characters not handled by `encodeURIComponent` (to follow RFC 3986)
            percentEncoded = percentEncoded.replace(/[!'()]/g, escape);

            // %-encode characters not handled by `escape` (to follow RFC 3986)
            percentEncoded = percentEncoded.replace(/\*/g, "%2A");

            // replace percent-encoded spaces with a "+"
            return percentEncoded.replace(/%20/g, "+");
        }
    };
}());
<commit_msg>Fixed jslint note about trailing whitespace<commit_after>/*globals qq */
qq.s3 = qq.s3 || {};

qq.s3.util = qq.s3.util || (function() {
    "use strict";

    return {
        AWS_PARAM_PREFIX: "x-amz-meta-",

        SESSION_TOKEN_PARAM_NAME: "x-amz-security-token",

        REDUCED_REDUNDANCY_PARAM_NAME: "x-amz-storage-class",
        REDUCED_REDUNDANCY_PARAM_VALUE: "REDUCED_REDUNDANCY",

        SERVER_SIDE_ENCRYPTION_PARAM_NAME: "x-amz-server-side-encryption",
        SERVER_SIDE_ENCRYPTION_PARAM_VALUE: "AES256",

        /**
         * This allows for the region to be specified in the bucket's endpoint URL, or not.
         *
         * Examples of some valid endpoints are:
         *     http://foo.s3.amazonaws.com
         *     https://foo.s3.amazonaws.com
         *     http://foo.s3-ap-northeast-1.amazonaws.com
         *     foo.s3.amazonaws.com
         *     http://foo.bar.com
         *     http://s3.amazonaws.com/foo.bar.com
         * ...etc
         *
         * @param endpoint The bucket's URL.
         * @returns {String || undefined} The bucket name, or undefined if the URL cannot be parsed.
         */
        getBucket: function(endpoint) {
            var patterns = [
                    //bucket in domain
                    /^(?:https?:\/\/)?([a-z0-9.\-_]+)\.s3(?:-[a-z0-9\-]+)?\.amazonaws\.com/i,
                    //bucket in path
                    /^(?:https?:\/\/)?s3(?:-[a-z0-9\-]+)?\.amazonaws\.com\/([a-z0-9.\-_]+)/i,
                    //custom domain
                    /^(?:https?:\/\/)?([a-z0-9.\-_]+)/i
                ],
                bucket;

            qq.each(patterns, function(idx, pattern) {
                var match = pattern.exec(endpoint);

                if (match) {
                    bucket = match[1];
                    return false;
                }
            });

            return bucket;
        },

        /** Create Prefixed request headers which are appropriate for S3.
         *
         * If the request header is appropriate for S3 (e.g. Cache-Control) then pass
         * it along without a metadata prefix. For all other request header parameter names,
         * apply qq.s3.util.AWS_PARAM_PREFIX before the name.
         *
         * @param name Name of the Request Header parameter to construct a (possibly) prefixed name.
         * @returns {String} A valid Request Header parameter name.
         */
        _getPrefixedParamName: function (name) {
            switch (name)
            {
                //
                // Valid request headers (not sent by fine-uploader) which should be returned as-is (case-sensitive)
                // see: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html
                //
                case "Cache-Control":
                case "Content-Disposition":
                case "Content-Encoding":
                case "Content-MD5": // CAW: Content-MD5 might not be appropriate from user-land
                case "x-amz-server-side-encryption-customer-algorithm":
                case "x-amz-server-side-encryption-customer-key":
                case "x-amz-server-side-encryption-customer-key-MD5":
                    return name;
                default:
                    return qq.s3.util.AWS_PARAM_PREFIX + name;
            }
        },

        /**
         * Create a policy document to be signed and sent along with the S3 upload request.
         *
         * @param spec Object with properties use to construct the policy document.
         * @returns {Object} Policy doc.
         */
        getPolicy: function(spec) {
            var policy = {},
                conditions = [],
                bucket = qq.s3.util.getBucket(spec.endpoint),
                key = spec.key,
                acl = spec.acl,
                type = spec.type,
                expirationDate = new Date(),
                expectedStatus = spec.expectedStatus,
                sessionToken = spec.sessionToken,
                params = spec.params,
                successRedirectUrl = qq.s3.util.getSuccessRedirectAbsoluteUrl(spec.successRedirectUrl),
                minFileSize = spec.minFileSize,
                maxFileSize = spec.maxFileSize,
                reducedRedundancy = spec.reducedRedundancy,
                serverSideEncryption = spec.serverSideEncryption;

            policy.expiration = qq.s3.util.getPolicyExpirationDate(expirationDate);

            conditions.push({acl: acl});
            conditions.push({bucket: bucket});

            if (type) {
                conditions.push({"Content-Type": type});
            }

            if (expectedStatus) {
                conditions.push({success_action_status: expectedStatus.toString()});
            }

            if (successRedirectUrl) {
                conditions.push({success_action_redirect: successRedirectUrl});
            }

            if (reducedRedundancy) {
                conditions.push({});
                conditions[conditions.length - 1][qq.s3.util.REDUCED_REDUNDANCY_PARAM_NAME] = qq.s3.util.REDUCED_REDUNDANCY_PARAM_VALUE;
            }

            if (sessionToken) {
                conditions.push({});
                conditions[conditions.length - 1][qq.s3.util.SESSION_TOKEN_PARAM_NAME] = sessionToken;
            }

            if (serverSideEncryption) {
                conditions.push({});
                conditions[conditions.length - 1][qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_NAME] = qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_VALUE;
            }

            conditions.push({key: key});

            // user metadata
            qq.each(params, function(name, val) {
                var awsParamName = qq.s3.util._getPrefixedParamName(name),
                    param = {};

                param[awsParamName] = encodeURIComponent(val);
                conditions.push(param);
            });

            policy.conditions = conditions;

            qq.s3.util.enforceSizeLimits(policy, minFileSize, maxFileSize);

            return policy;
        },

        /**
         * Update a previously constructed policy document with updated credentials.  Currently, this only requires we
         * update the session token.  This is only relevant if requests are being signed client-side.
         *
         * @param policy Live policy document
         * @param newSessionToken Updated session token.
         */
        refreshPolicyCredentials: function(policy, newSessionToken) {
            var sessionTokenFound = false;

            qq.each(policy.conditions, function(oldCondIdx, oldCondObj) {
                qq.each(oldCondObj, function(oldCondName, oldCondVal) {
                    if (oldCondName === qq.s3.util.SESSION_TOKEN_PARAM_NAME) {
                        oldCondObj[oldCondName] = newSessionToken;
                        sessionTokenFound = true;
                    }
                });
            });

            if (!sessionTokenFound) {
                policy.conditions.push({});
                policy.conditions[policy.conditions.length - 1][qq.s3.util.SESSION_TOKEN_PARAM_NAME] = newSessionToken;
            }
        },

        /**
         * Generates all parameters to be passed along with the S3 upload request.  This includes invoking a callback
         * that is expected to asynchronously retrieve a signature for the policy document.  Note that the server
         * signing the request should reject a "tainted" policy document that includes unexpected values, since it is
         * still possible for a malicious user to tamper with these values during policy document generation,
         * before it is sent to the server for signing.
         *
         * @param spec Object with properties: `params`, `type`, `key`, `accessKey`, `acl`, `expectedStatus`, `successRedirectUrl`,
         * `reducedRedundancy`, serverSideEncryption, and `log()`, along with any options associated with `qq.s3.util.getPolicy()`.
         * @returns {qq.Promise} Promise that will be fulfilled once all parameters have been determined.
         */
        generateAwsParams: function(spec, signPolicyCallback) {
            var awsParams = {},
                customParams = spec.params,
                promise = new qq.Promise(),
                policyJson = qq.s3.util.getPolicy(spec),
                sessionToken = spec.sessionToken,
                type = spec.type,
                key = spec.key,
                accessKey = spec.accessKey,
                acl = spec.acl,
                expectedStatus = spec.expectedStatus,
                successRedirectUrl = qq.s3.util.getSuccessRedirectAbsoluteUrl(spec.successRedirectUrl),
                reducedRedundancy = spec.reducedRedundancy,
                serverSideEncryption = spec.serverSideEncryption,
                log = spec.log;

            awsParams.key = key;
            awsParams.AWSAccessKeyId = accessKey;

            if (type) {
                awsParams["Content-Type"] = type;
            }

            if (expectedStatus) {
                awsParams.success_action_status = expectedStatus;
            }

            if (successRedirectUrl) {
                awsParams.success_action_redirect = successRedirectUrl;
            }

            if (reducedRedundancy) {
                awsParams[qq.s3.util.REDUCED_REDUNDANCY_PARAM_NAME] = qq.s3.util.REDUCED_REDUNDANCY_PARAM_VALUE;
            }

            if (serverSideEncryption) {
                awsParams[qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_NAME] = qq.s3.util.SERVER_SIDE_ENCRYPTION_PARAM_VALUE;
            }

            if (sessionToken) {
                awsParams[qq.s3.util.SESSION_TOKEN_PARAM_NAME] = sessionToken;
            }

            awsParams.acl = acl;

            // Custom (user-supplied) params must be prefixed with the value of `qq.s3.util.AWS_PARAM_PREFIX`.
            // Params such as Cache-Control or Content-Disposition will not be prefixed.
            // All param values will be URI encoded as well.
            qq.each(customParams, function(name, val) {
                var awsParamName = qq.s3.util._getPrefixedParamName(name);
                awsParams[awsParamName] = encodeURIComponent(val);
            });

            // Invoke a promissory callback that should provide us with a base64-encoded policy doc and an
            // HMAC signature for the policy doc.
            signPolicyCallback(policyJson).then(
                function(policyAndSignature, updatedAccessKey, updatedSessionToken) {
                    awsParams.policy = policyAndSignature.policy;
                    awsParams.signature = policyAndSignature.signature;

                    if (updatedAccessKey) {
                        awsParams.AWSAccessKeyId = updatedAccessKey;
                    }
                    if (updatedSessionToken) {
                        awsParams[qq.s3.util.SESSION_TOKEN_PARAM_NAME] = updatedSessionToken;
                    }

                    promise.success(awsParams);
                },
                function(errorMessage) {
                    errorMessage = errorMessage || "Can't continue further with request to S3 as we did not receive " +
                                                   "a valid signature and policy from the server.";

                    log("Policy signing failed.  " + errorMessage, "error");
                    promise.failure(errorMessage);
                }
            );

            return promise;
        },

        /**
         * Add a condition to an existing S3 upload request policy document used to ensure AWS enforces any size
         * restrictions placed on files server-side.  This is important to do, in case users mess with the client-side
         * checks already in place.
         *
         * @param policy Policy document as an `Object`, with a `conditions` property already attached
         * @param minSize Minimum acceptable size, in bytes
         * @param maxSize Maximum acceptable size, in bytes (0 = unlimited)
         */
        enforceSizeLimits: function(policy, minSize, maxSize) {
            var adjustedMinSize = minSize < 0 ? 0 : minSize,
                // Adjust a maxSize of 0 to the largest possible integer, since we must specify a high and a low in the request
                adjustedMaxSize = maxSize <= 0 ? 9007199254740992 : maxSize;

            if (minSize > 0 || maxSize > 0) {
                policy.conditions.push(["content-length-range", adjustedMinSize.toString(), adjustedMaxSize.toString()]);
            }
        },

        getPolicyExpirationDate: function(date) {
            /*jshint -W014 */
            // Is this going to be a problem if we encounter this moments before 2 AM just before daylight savings time ends?
            date.setMinutes(date.getMinutes() + 5);

            if (Date.prototype.toISOString) {
                return date.toISOString();
            }
            else {
                var pad = function(number) {
                    var r = String(number);

                    if ( r.length === 1 ) {
                        r = "0" + r;
                    }

                    return r;
                };

                return date.getUTCFullYear()
                        + "-" + pad( date.getUTCMonth() + 1 )
                        + "-" + pad( date.getUTCDate() )
                        + "T" + pad( date.getUTCHours() )
                        + ":" + pad( date.getUTCMinutes() )
                        + ":" + pad( date.getUTCSeconds() )
                        + "." + String( (date.getUTCMilliseconds()/1000).toFixed(3) ).slice( 2, 5 )
                        + "Z";
            }
        },

        /**
         * Looks at a response from S3 contained in an iframe and parses the query string in an attempt to identify
         * the associated resource.
         *
         * @param iframe Iframe containing response
         * @returns {{bucket: *, key: *, etag: *}}
         */
        parseIframeResponse: function(iframe) {
            var doc = iframe.contentDocument || iframe.contentWindow.document,
                queryString = doc.location.search,
                match = /bucket=(.+)&key=(.+)&etag=(.+)/.exec(queryString);

            if (match) {
                return {
                    bucket: match[1],
                    key: match[2],
                    etag: match[3].replace(/%22/g, "")
                };
            }
        },

        /**
         * @param successRedirectUrl Relative or absolute location of success redirect page
         * @returns {*|string} undefined if the parameter is undefined, otherwise the absolute location of the success redirect page
         */
        getSuccessRedirectAbsoluteUrl: function(successRedirectUrl) {
            if (successRedirectUrl) {
                var targetAnchorContainer = document.createElement("div"),
                    targetAnchor;

                if (qq.ie7()) {
                    // Note that we must make use of `innerHTML` for IE7 only instead of simply creating an anchor via
                    // `document.createElement('a')` and setting the `href` attribute.  The latter approach does not allow us to
                    // obtain an absolute URL in IE7 if the `endpoint` is a relative URL.
                    targetAnchorContainer.innerHTML = "";
                    targetAnchor = targetAnchorContainer.firstChild;
                    return targetAnchor.href;
                }
                else {
                    // IE8 and IE9 do not seem to derive an absolute URL from a relative URL using the `innerHTML`
                    // approach above, so we'll just create an anchor this way and set it's `href` attribute.
                    // Due to yet another quirk in IE8 and IE9, we have to set the `href` equal to itself
                    // in order to ensure relative URLs will be properly parsed.
                    targetAnchor = document.createElement("a");
                    targetAnchor.href = successRedirectUrl;
                    targetAnchor.href = targetAnchor.href;
                    return targetAnchor.href;
                }
            }
        },

        // AWS employs a strict interpretation of [RFC 3986](http://tools.ietf.org/html/rfc3986#page-12).
        // So, we must ensure all reserved characters listed in the spec are percent-encoded,
        // and spaces are replaced with "+".
        encodeQueryStringParam: function(param) {
            var percentEncoded = encodeURIComponent(param);

            // %-encode characters not handled by `encodeURIComponent` (to follow RFC 3986)
            percentEncoded = percentEncoded.replace(/[!'()]/g, escape);

            // %-encode characters not handled by `escape` (to follow RFC 3986)
            percentEncoded = percentEncoded.replace(/\*/g, "%2A");

            // replace percent-encoded spaces with a "+"
            return percentEncoded.replace(/%20/g, "+");
        }
    };
}());
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'

import { GEODATA_API_URL } from '@env'

const Thumbnail = ({ recordId, thumbnails, t }) => {
  const hasThumbnail = thumbnails && thumbnails.length > 0
  const thumbnail = hasThumbnail
    ? `${GEODATA_API_URL}/records/${recordId}/thumbnails/${thumbnails[0].originalUrlHash}`
    : '/static/images/datasets/default-thumbnail.svg'

  return (

  )
}

Thumbnail.propTypes = {
  thumbnails: PropTypes.arrayOf(PropTypes.shape({
    originalUrlHash: PropTypes.isRequired
  })),
  recordId: PropTypes.string.isRequired,

  t: PropTypes.func.isRequired
}

export default translate('search')(Thumbnail)
<commit_msg>Fix search result image sizes<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'

import { GEODATA_API_URL } from '@env'

const Thumbnail = ({ recordId, thumbnails, t }) => {
  const hasThumbnail = thumbnails && thumbnails.length > 0
  const thumbnail = hasThumbnail
    ? `${GEODATA_API_URL}/records/${recordId}/thumbnails/${thumbnails[0].originalUrlHash}`
    : '/static/images/datasets/default-thumbnail.svg'

  return (

  )
}

Thumbnail.propTypes = {
  thumbnails: PropTypes.arrayOf(PropTypes.shape({
    originalUrlHash: PropTypes.isRequired
  })),
  recordId: PropTypes.string.isRequired,

  t: PropTypes.func.isRequired
}

export default translate('search')(Thumbnail)
<|endoftext|>


<commit_before>'use strict';

/* globals constants */
angular.module('htckApp').factory('hPages', function (hExport, hSave, hElement, $timeout, $mdDialog, $log) {
  var scope = {};
  this.pages = [];
  this.pngPages = [];
  this.currentPageIndex=0;
  this.gifInterval = 1;
  this.gifWidth = constants.W / 2;

  this.init = function(parent){
    scope = parent.$new();
    this.currentPageIndex = 0;
  };

  this.saveCurrent = function() {
    scope.$parent.unfocus();
    var json = hExport.exportOneJSON(scope.$parent.paper);
    var pageIndex = this.currentPageIndex;
    var hPages = this;
    hExport.exportOneBase64(constants.RAPHAEL_PAPER, 'canvas', scope.$parent.paper, function(base64){
      hPages.pngPages[pageIndex] = base64;
    });

    this.pages[this.currentPageIndex] = json;
  };

  this.goto = function(idx) {
    if(this.pages[idx]) {
      scope.$parent.setBrush(undefined);
      this.currentPageIndex = idx;
      hSave.import(this.pages[idx]);
    }
    // TODO else Error ?
  };

  this.next = function() {
    this.saveCurrent();
    this.goto(this.currentPageIndex + 1);
  };

  this.prev = function() {
    this.saveCurrent();
    this.goto(this.currentPageIndex - 1);
  };

  this.create = function(json) {
    this.pages.splice(this.currentPageIndex+1, 0, json);
    this.goto(this.currentPageIndex+1);
  };

  this.createByCopy = function() {
    this.saveCurrent();
    this.create(this.pages[this.currentPageIndex]);
  };

  this.createNew = function() {
    this.saveCurrent();
    this.create(hExport.exportOneJSON(scope.$parent.paper, true));
  };

  this.clearPage = function() {
    var elemList = [];
    scope.$parent.paper.forEach(function (elem) {
      if (elem.ft&&!elem.background) {
        elemList.push(elem);
      }
    });
    for (var i = 0; i < elemList.length ; ++i) {
      hElement.remove(elemList[i]);
    }
  };

  this.deletePage = function() {
    this.pages.splice(this.currentPageIndex, 1);
    this.pngPages.splice(this.currentPageIndex, 1);
    this.currentPageIndex = (this.currentPageIndex <= 0) ? (0) : (this.currentPageIndex - 1);
    this.goto(this.currentPageIndex);
  };

  function gifDialogController($scope, hPages){
    $scope.hPages = hPages;
    $scope.constants = constants;

    $scope.cancel = function() {
      $mdDialog.cancel();
    };
    $scope.answer = function() {
      $mdDialog.hide({});
    };
  }

  this.exportGIF = function(ev) {
    if(this.pages.length <= 1){
      return;
    }
    this.saveCurrent();
    var hPages = this;
    $mdDialog.show({
      controller: gifDialogController,
      templateUrl: 'views/gifsettings.html',
      parent: angular.element(document.body),
      targetEvent: ev,
      clickOutsideToClose:true,
      locals : {
      }
    })
    .then(function() {
      scope.$parent.exportGIFRunning = true;
      hExport.exportManyGIF(hPages.pngPages, 'TheLegendaryTaleOfBayeux.gif', 'canvas', hPages.gifWidth, hPages.gifWidth * constants.H / constants.W, hPages.gifInterval, function(){
        scope.$parent.exportGIFRunning = false;
      });
    }, function() {
      $log.debug('Cancel');
    });
    /*
    $timeout(function(){
      hExport.exportManyGIF(hPages.pngPages, 'TheLegendaryTaleOfBayeux.gif', 'canvas');
    },200);*/
  };

  return {
    init: this.init,
    pages: this.pages,
    pngPages: this.pngPages,
    currentPageIndex: this.currentPageIndex,
    gifInterval: this.gifInterval,
    gifWidth: this.gifWidth,

    saveCurrent: this.saveCurrent,
    goto: this.goto,
    create: this.create,
    delete: this.deletePage,
    next: this.next,
    prev: this.prev,
    createByCopy: this.createByCopy,
    createNew: this.createNew,
    exportGIF: this.exportGIF,
    clearPage: this.clearPage
  };
});<commit_msg>Fix page clear with copied objects<commit_after>'use strict';

/* globals constants */
angular.module('htckApp').factory('hPages', function (hExport, hSave, hElement, $timeout, $mdDialog, $log) {
  var scope = {};
  this.pages = [];
  this.pngPages = [];
  this.currentPageIndex=0;
  this.gifInterval = 1;
  this.gifWidth = constants.W / 2;

  this.init = function(parent){
    scope = parent.$new();
    this.currentPageIndex = 0;
  };

  this.saveCurrent = function() {
    scope.$parent.unfocus();
    var json = hExport.exportOneJSON(scope.$parent.paper);
    var pageIndex = this.currentPageIndex;
    var hPages = this;
    hExport.exportOneBase64(constants.RAPHAEL_PAPER, 'canvas', scope.$parent.paper, function(base64){
      hPages.pngPages[pageIndex] = base64;
    });

    this.pages[this.currentPageIndex] = json;
  };

  this.goto = function(idx) {
    if(this.pages[idx]) {
      scope.$parent.setBrush(undefined);
      this.currentPageIndex = idx;
      hSave.import(this.pages[idx]);
    }
    // TODO else Error ?
  };

  this.next = function() {
    this.saveCurrent();
    this.goto(this.currentPageIndex + 1);
  };

  this.prev = function() {
    this.saveCurrent();
    this.goto(this.currentPageIndex - 1);
  };

  this.create = function(json) {
    this.pages.splice(this.currentPageIndex+1, 0, json);
    this.goto(this.currentPageIndex+1);
  };

  this.createByCopy = function() {
    this.saveCurrent();
    this.create(this.pages[this.currentPageIndex]);
  };

  this.createNew = function() {
    this.saveCurrent();
    this.create(hExport.exportOneJSON(scope.$parent.paper, true));
  };

  this.clearPage = function() {
    scope.$parent.unfocus();

    var elemList = [];
    scope.$parent.paper.forEach(function (elem) {
      if (elem.ft && !elem.background && !elem.tmpClone) {
        elemList.push(elem);
      }
    });
    for (var i = 0; i < elemList.length ; ++i) {
      hElement.remove(elemList[i]);
    }
  };

  this.deletePage = function() {
    this.pages.splice(this.currentPageIndex, 1);
    this.pngPages.splice(this.currentPageIndex, 1);
    this.currentPageIndex = (this.currentPageIndex <= 0) ? (0) : (this.currentPageIndex - 1);
    this.goto(this.currentPageIndex);
  };

  function gifDialogController($scope, hPages){
    $scope.hPages = hPages;
    $scope.constants = constants;

    $scope.cancel = function() {
      $mdDialog.cancel();
    };
    $scope.answer = function() {
      $mdDialog.hide({});
    };
  }

  this.exportGIF = function(ev) {
    if(this.pages.length <= 1){
      return;
    }
    this.saveCurrent();
    var hPages = this;
    $mdDialog.show({
      controller: gifDialogController,
      templateUrl: 'views/gifsettings.html',
      parent: angular.element(document.body),
      targetEvent: ev,
      clickOutsideToClose:true,
      locals : {
      }
    })
    .then(function() {
      scope.$parent.exportGIFRunning = true;
      hExport.exportManyGIF(hPages.pngPages, 'TheLegendaryTaleOfBayeux.gif', 'canvas', hPages.gifWidth, hPages.gifWidth * constants.H / constants.W, hPages.gifInterval, function(){
        scope.$parent.exportGIFRunning = false;
      });
    }, function() {
      $log.debug('Cancel');
    });
    /*
    $timeout(function(){
      hExport.exportManyGIF(hPages.pngPages, 'TheLegendaryTaleOfBayeux.gif', 'canvas');
    },200);*/
  };

  return {
    init: this.init,
    pages: this.pages,
    pngPages: this.pngPages,
    currentPageIndex: this.currentPageIndex,
    gifInterval: this.gifInterval,
    gifWidth: this.gifWidth,

    saveCurrent: this.saveCurrent,
    goto: this.goto,
    create: this.create,
    delete: this.deletePage,
    next: this.next,
    prev: this.prev,
    createByCopy: this.createByCopy,
    createNew: this.createNew,
    exportGIF: this.exportGIF,
    clearPage: this.clearPage
  };
});<|endoftext|>


<commit_before>import env from '../shared/Env';

const Author = 'Pablo Magaz';
const SiteTitle = Author;
const SiteDescription = Author;
const BlogTitle = 'El Blog Isomórfico';
const BlogDescription = 'JavaScript, programación y mas';
const numPosts = 10;
const postOpeningChars= 46;
const codeHighlightDelay = 300;
const postOpeningSplitChar = '@@@';
const GoogleAnaliticsId = 'aaaaa';

let HostName;
let ServerUrl;
let SiteUrl;
let BlogUrl;
let GhostUrl;
let ImageUrl;
let BaseApiUrl;
let PostsApiUrl;
let clientSecret;
let PostApiUrl;
let PostsApi;
let PostApi;
let ContentPath;
let Ssl;
let keysPath;
let Protocol;

if (env === 'development') {
  HostName = 'localhost';
  ServerUrl = `http://${ HostName }`;
  clientSecret = '285ee4eda6c3';
  SiteUrl = `${ ServerUrl }:8000`;
  BlogUrl = `${ SiteUrl }/blog`;
  GhostUrl = `${ ServerUrl }:2369`;
  ImageUrl = GhostUrl;
  BlogUrl = `${ GhostUrl }/blog/`;
  BaseApiUrl = `${ GhostUrl }/ghost/api/v0.1/`;
  PostApiUrl = `${ SiteUrl }/api/post/`;
  PostsApiUrl = `${ SiteUrl }/api/posts/`;
  PostsApi = `${ BaseApiUrl }posts/?client_id=ghost-frontend&client_secret=${clientSecret}&include=tags&fields=id,uuid,title,slug,html,image,feature_image,tags,updated_at,updated_at,published_at&order=published_at desc&limit=${numPosts}`;
  PostApi = `${ BaseApiUrl }posts/slug/:slug/?client_id=ghost-frontend&client_secret=${clientSecret}&include=tags`;
  Ssl = true;
  keysPath = null;
} else {
  Ssl = true;
  Protocol = Ssl ? 'https://' : 'http://';
  HostName = '188.166.49.134';
  ServerUrl = `http://${ HostName }`;
  SiteUrl = `${ ServerUrl }`;
  clientSecret = 'd7d4f5b6725d';
  BlogUrl = `${ SiteUrl }/blog`;
  GhostUrl = `${ ServerUrl }:2369`;
  BlogUrl = `${ GhostUrl }/blog/`;
  ImageUrl = `${Protocol}${ HostName }`;
  ContentPath = '/var/www/ghost/content';
  BaseApiUrl = `${ GhostUrl }/ghost/api/v0.1/`;
  PostApiUrl = `${Protocol}${ HostName }/api/post/`;
  PostsApiUrl = `${Protocol}${ HostName }/api/posts/`;
  PostsApi = `${ BaseApiUrl }posts/?client_id=ghost-frontend&client_secret=${clientSecret}&include=tags&fields=id,uuid,title,slug,html,image,feature_image,tags,updated_at,updated_at,published_at&order=published_at desc&limit=${numPosts}`;
  PostApi = `${ BaseApiUrl }posts/slug/:slug/?client_id=ghost-frontend&client_secret=${clientSecret}&include=tags`;
  keysPath = '';
}

export const SiteConf = { Author, SiteTitle, SiteUrl, BlogDescription, BlogTitle, BlogUrl, SiteDescription, ImageUrl, ContentPath, PostApi, PostsApi, PostApiUrl, PostsApiUrl, postOpeningChars, postOpeningSplitChar, codeHighlightDelay, GoogleAnaliticsId, Ssl, keysPath};
<commit_msg>Set ssl to false<commit_after>import env from '../shared/Env';

const Author = 'Pablo Magaz';
const SiteTitle = Author;
const SiteDescription = Author;
const BlogTitle = 'El Blog Isomórfico';
const BlogDescription = 'JavaScript, programación y mas';
const numPosts = 10;
const postOpeningChars= 46;
const codeHighlightDelay = 300;
const postOpeningSplitChar = '@@@';
const GoogleAnaliticsId = 'aaaaa';

let HostName;
let ServerUrl;
let SiteUrl;
let BlogUrl;
let GhostUrl;
let ImageUrl;
let BaseApiUrl;
let PostsApiUrl;
let clientSecret;
let PostApiUrl;
let PostsApi;
let PostApi;
let ContentPath;
let Ssl;
let keysPath;
let Protocol;

if (env === 'development') {
  HostName = 'localhost';
  ServerUrl = `http://${ HostName }`;
  clientSecret = '285ee4eda6c3';
  SiteUrl = `${ ServerUrl }:8000`;
  BlogUrl = `${ SiteUrl }/blog`;
  GhostUrl = `${ ServerUrl }:2369`;
  ImageUrl = GhostUrl;
  BlogUrl = `${ GhostUrl }/blog/`;
  BaseApiUrl = `${ GhostUrl }/ghost/api/v0.1/`;
  PostApiUrl = `${ SiteUrl }/api/post/`;
  PostsApiUrl = `${ SiteUrl }/api/posts/`;
  PostsApi = `${ BaseApiUrl }posts/?client_id=ghost-frontend&client_secret=${clientSecret}&include=tags&fields=id,uuid,title,slug,html,image,feature_image,tags,updated_at,updated_at,published_at&order=published_at desc&limit=${numPosts}`;
  PostApi = `${ BaseApiUrl }posts/slug/:slug/?client_id=ghost-frontend&client_secret=${clientSecret}&include=tags`;
  Ssl = true;
  keysPath = null;
} else {
  Ssl = false;
  Protocol = Ssl ? 'https://' : 'http://';
  HostName = '188.166.49.134';
  ServerUrl = `http://${ HostName }`;
  SiteUrl = `${ ServerUrl }`;
  clientSecret = 'd7d4f5b6725d';
  BlogUrl = `${ SiteUrl }/blog`;
  GhostUrl = `${ ServerUrl }:2369`;
  BlogUrl = `${ GhostUrl }/blog/`;
  ImageUrl = `${Protocol}${ HostName }`;
  ContentPath = '/var/www/ghost/content';
  BaseApiUrl = `${ GhostUrl }/ghost/api/v0.1/`;
  PostApiUrl = `${Protocol}${ HostName }/api/post/`;
  PostsApiUrl = `${Protocol}${ HostName }/api/posts/`;
  PostsApi = `${ BaseApiUrl }posts/?client_id=ghost-frontend&client_secret=${clientSecret}&include=tags&fields=id,uuid,title,slug,html,image,feature_image,tags,updated_at,updated_at,published_at&order=published_at desc&limit=${numPosts}`;
  PostApi = `${ BaseApiUrl }posts/slug/:slug/?client_id=ghost-frontend&client_secret=${clientSecret}&include=tags`;
  keysPath = '';
}

export const SiteConf = { Author, SiteTitle, SiteUrl, BlogDescription, BlogTitle, BlogUrl, SiteDescription, ImageUrl, ContentPath, PostApi, PostsApi, PostApiUrl, PostsApiUrl, postOpeningChars, postOpeningSplitChar, codeHighlightDelay, GoogleAnaliticsId, Ssl, keysPath};
<|endoftext|>


<commit_before>/* global window, document, fetch */
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, hashHistory } from 'react-router';
import { Members, Provider as CoreProvider, Core } from 'oicr-ui-core';

const { transformJSON } = Members.helpers;

// Load store.
const store = require('../site/store').default;

const client = Core.initApolloClient(true, store);

// Render Members
const targetMembers = document.getElementById('app-members');

if (targetMembers) {
    // Check if static data is provided
    const staticMembers = transformJSON(
        targetMembers.getAttribute('data-members')
    );

    if (staticMembers) {
        store.dispatch({ type: 'RECEIVE_MEMBERS', staticMembers });
    }

    ReactDOM.render(
        <CoreProvider store={store} client={client}>
            <Router history={hashHistory}>
                <Members.BaseRoutes isStatic={!!staticMembers} />
            </Router>
        </CoreProvider>,
        targetMembers
    );
}
<commit_msg>Members fix<commit_after>/* global window, document, fetch */
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, hashHistory } from 'react-router';
import { Members, Provider as CoreProvider, Core } from 'oicr-ui-core';

const { transformJSON } = Members.helpers;

// Load store.
const store = require('../site/store').default;

const client = Core.initApolloClient(true, store);

// Render Members
const targetMembers = document.getElementById('app-members');

if (targetMembers) {
    // Check if static data is provided
    const staticMembers = transformJSON(
        targetMembers.getAttribute('data-members')
    );

    if (staticMembers) {
        store.dispatch({ type: 'RECEIVE_MEMBERS', data: staticMembers });
    }

    ReactDOM.render(
        <CoreProvider store={store} client={client}>
            <Router history={hashHistory}>
                <Members.BaseRoutes isStatic={!!staticMembers} />
            </Router>
        </CoreProvider>,
        targetMembers
    );
}
<|endoftext|>


<commit_before>import getRange from 'shared/parsing/getRangeForDygraph'

describe('getRangeForDygraphSpec', () => {
  it('gets the range for one timeSeries', () => {
    const timeSeries = [[new Date(1000), 1], [new Date(2000), 2], [new Date(3000), 3]]

    const actual = getRange(timeSeries)
    const expected = [1, 3]

    expect(actual).to.deep.equal(expected)
  })

  it('does not get range when a range is provided', () => {
    const timeSeries = [[new Date(1000), 1], [new Date(2000), 2], [new Date(3000), 3]]

    const providedRange = [0, 4]
    const actual = getRange(timeSeries, providedRange)

    expect(actual).to.deep.equal(providedRange)
  })

  it('gets the range for multiple timeSeries', () => {
    const timeSeries = [
      [new Date(1000), null, 1],
      [new Date(1000), 100, 1],
      [new Date(2000), null, 2],
      [new Date(3000), 200, 3],
    ]

    const actual = getRange(timeSeries)
    const expected = [1, 200]

    expect(actual).to.deep.equal(expected)
  })

  it('returns a null array of two elements when min and max are equal', () => {
    const timeSeries = [[new Date(1000), 1], [new Date(2000), 1], [new Date(3000), 1]]
    const actual = getRange(timeSeries)
    const expected = [null, null]

    expect(actual).to.deep.equal(expected)
  })

  describe('when user provides a Kapacitor rule value', () => {
    const defaultMax = 20
    const defaultMin = -10
    const timeSeries = [[new Date(1000), defaultMax], [new Date(2000), 1], [new Date(3000), defaultMin]]

    it('can pad positive values', () => {
      const kapacitorRuleValues = {value: 20, rangeValue: "", operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.equal(defaultMin)
      expect(max).to.be.above(defaultMax)
    })

    it('can pad negative values', () => {
      const kapacitorRuleValues = {value: -10, rangeValue: "", operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.be.below(defaultMin)
      expect(max).to.equal(defaultMax)
    })

    it('subtracts from positive values if Kapactior operator is "lower than"', () => {
      // TODO: go home
    })
  })

  describe('when user provides a Kapacitor rule rangeValue', () => {
    const defaultMax = 20
    const defaultMin = -10
    const timeSeries = [[new Date(1000), defaultMax], [new Date(2000), 1], [new Date(3000), defaultMin]]

    it('can pad positive values', () => {
      const kapacitorRuleValues = {value: null, rangeValue: 20, operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.equal(defaultMin)
      expect(max).to.be.above(defaultMax)
    })

    it('can pad negative values', () => {
      const kapacitorRuleValues = {value: null, rangeValue: -10, operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.be.below(defaultMin)
      expect(max).to.equal(defaultMax)
    })
  })
})
<commit_msg>Add tests<commit_after>import getRange from 'shared/parsing/getRangeForDygraph'

describe('getRangeForDygraphSpec', () => {
  it('gets the range for one timeSeries', () => {
    const timeSeries = [[new Date(1000), 1], [new Date(2000), 2], [new Date(3000), 3]]

    const actual = getRange(timeSeries)
    const expected = [1, 3]

    expect(actual).to.deep.equal(expected)
  })

  it('does not get range when a range is provided', () => {
    const timeSeries = [[new Date(1000), 1], [new Date(2000), 2], [new Date(3000), 3]]

    const providedRange = [0, 4]
    const actual = getRange(timeSeries, providedRange)

    expect(actual).to.deep.equal(providedRange)
  })

  it('gets the range for multiple timeSeries', () => {
    const timeSeries = [
      [new Date(1000), null, 1],
      [new Date(1000), 100, 1],
      [new Date(2000), null, 2],
      [new Date(3000), 200, 3],
    ]

    const actual = getRange(timeSeries)
    const expected = [1, 200]

    expect(actual).to.deep.equal(expected)
  })

  it('returns a null array of two elements when min and max are equal', () => {
    const timeSeries = [[new Date(1000), 1], [new Date(2000), 1], [new Date(3000), 1]]
    const actual = getRange(timeSeries)
    const expected = [null, null]

    expect(actual).to.deep.equal(expected)
  })

  describe('when user provides a Kapacitor rule value', () => {
    const defaultMax = 20
    const defaultMin = -10
    const positiveMin = 10
    const timeSeries = [[new Date(1000), defaultMax], [new Date(2000), 1], [new Date(3000), defaultMin]]
    const timeSeries1 = [[new Date(1000), defaultMax], [new Date(2000), 1], [new Date(3000), positiveMin]]

    it('can pad positive values', () => {
      const kapacitorRuleValues = {value: 20, rangeValue: "", operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.equal(defaultMin)
      expect(max).to.be.above(defaultMax)
    })

    it('can pad negative values', () => {
      const kapacitorRuleValues = {value: -10, rangeValue: "", operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.be.below(defaultMin)
      expect(max).to.equal(defaultMax)
    })

    describe('when Kapacitor operator is "lower than" and value is outiside the range', () => {
      it('subtracts from a positive value', () => {
        const value = 5
        const rangeValue = ""
        const operator = "lower than"
        const kapacitorRuleValues = {value, rangeValue, operator}

        const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)
        expect(min).to.be.lessThan(value)
        expect(max).to.equal(defaultMax)
      })
    })
  })

  describe('when user provides a Kapacitor rule rangeValue', () => {
    const defaultMax = 20
    const defaultMin = -10
    const timeSeries = [[new Date(1000), defaultMax], [new Date(2000), 1], [new Date(3000), defaultMin]]

    it('can pad positive values', () => {
      const kapacitorRuleValues = {value: null, rangeValue: 20, operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.equal(defaultMin)
      expect(max).to.be.above(defaultMax)
    })

    it('can pad negative values', () => {
      const kapacitorRuleValues = {value: null, rangeValue: -10, operator: null}
      const [min, max] = getRange(timeSeries, undefined, kapacitorRuleValues)

      expect(min).to.be.below(defaultMin)
      expect(max).to.equal(defaultMax)
    })
  })
})
<|endoftext|>


<commit_before>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit_time_hour, commit_time_min, commit_time_sec)
    var t_offset = 0
    if (process.env.FAKETIME) {
      // 20% buffer, to reduce the likelihood of going in to the future
      t_offset = commit_time * -1.2;
      faketime(t_offset);
    }
    try {
      child_process.execFileSync('git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var counter = 0
    process.stdout.write("Queueing up the first commit...\r");
    var timeout;
    if (process.env.FAKETIME) {
      faketime(t_offset);
      timeout = 20;
    } else {
      timeout = randomIntFromInterval(0, max_sleep) * 1000
    }
    setTimeout(commit, timeout, t_offset, outFD, outFile, data, counter, length);
  })
} else {
  console.info("Usage:")
  console.info("node commit.js infile outfile [max_time_in_seconds] [step]")
}

function sleep(se<commit_msg>Really commit all the required changes<commit_after>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit_time_hour, commit_time_min, commit_time_sec)
    var t_offset = 0
    if (process.env.FAKETIME) {
      // 20% buffer, to reduce the likelihood of going in to the future
      t_offset = commit_time * -1.2;
      faketime(t_offset);
    }
    try {
      child_process.execFileSync('git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var counter = 0
    process.stdout.write("Queueing up the first commit...\r");
    var timeout;
    if (process.env.FAKETIME) {
      faketime(t_offset);
      timeout = 20;
    } else {
      timeout = randomIntFromInterval(0, max_sleep) * 1000
    }
    setTimeout(commit, timeout, t_offset, outFD, outFile, data, counter, length);
  })
} else {
  console.info("Usage:")
  console.info("node commit.js infile outfile [max_time_in_seconds] [step]")
}

function sleep(sec<|endoftext|>