<commit_before>/*global _, content, jQuery, MM, observable, window, document, setTimeout*/
MM.MapRepository = function (activityLog, alert, repositories) {
	// order of repositories is important, the first repository is default
	'use strict';
	observable(this);
	var dispatchEvent = this.dispatchEvent,
		mapInfo = {},
		chooseRepository = function (identifiers) {
			// order of identifiers is important, the first identifier takes precedence
			var idIndex, repoIndex;
			for (idIndex = 0; idIndex < identifiers.length; idIndex++) {
				for (repoIndex = 0; repoIndex < repositories.length; repoIndex++) {
					if (repositories[repoIndex].recognises(identifiers[idIndex])) {
						return repositories[repoIndex];
					}
				}
			}
			return repositories[0];
		},
		setMap = function (idea, mapId) {
			mapInfo = {
				idea: idea,
				mapId: mapId
			};
			dispatchEvent('mapLoaded', idea, mapId);
		},
		mapLoaded = function (fileContent, mapId, mimeType) {
			var json, idea;
			if (mimeType === 'application/json') {
				json = typeof fileContent === 'string' ? JSON.parse(fileContent) : fileContent;
			} else if (mimeType === 'application/octet-stream') {
				json = JSON.parse(fileContent);
			} else if (mimeType === 'application/x-freemind') {
				json = MM.freemindImport(fileContent);
			}
			idea = content(json);
			setMap(idea, mapId);
		},
		shouldRetry = function (retries) {
			var times = MM.retryTimes(retries);
			return function (status) {
				return times() && status === 'network-error';
			};
		};
	MM.MapRepository.mapLocationChange(this);
	MM.MapRepository.activityTracking(this, activityLog);

	MM.MapRepository.alerts(this, alert);
	MM.MapRepository.toolbarAndUnsavedChangesDialogue(this, activityLog);

	this.setMap = setMap;

	this.loadMap = function (mapId) {
		var timeout,
			repository = chooseRepository([mapId]),
			progressEvent = function (evt) {
				var done = (evt && evt.loaded) || 0,
					total = (evt && evt.total) || 1,
					message = ((evt && evt.loaded) ? Math.round(100 * done / total) + "%" : evt);
				dispatchEvent('mapLoading', mapId, message);
			},
			mapLoadFailed = function (reason, label) {
				var retryWithDialog = function () {
					dispatchEvent('mapLoading', mapId);
					repository.loadMap(mapId, true).then(mapLoaded, mapLoadFailed).progress(progressEvent);
				}, repositoryName = repository.description ? ' [' + repository.description + ']' : '';
				label = label ? label + repositoryName : repositoryName;
				if (reason === 'no-access-allowed') {
					dispatchEvent('mapLoadingUnAuthorized', mapId, reason);
				} else if (reason === 'failed-authentication') {
					dispatchEvent('authorisationFailed', repository.description, retryWithDialog);
				} else if (reason === 'not-authenticated') {
					dispatchEvent('authRequired', repository.description, retryWithDialog);
				} else {
					dispatchEvent('mapLoadingFailed', mapId, reason, label);
				}
			};
		dispatchEvent('mapLoading', mapId);
		MM.retry(
			repository.loadMap.bind(repository, mapId),
			shouldRetry(5),
			MM.linearBackoff()
		).then(
			mapLoaded,
			mapLoadFailed
		).progress(progressEvent);
	};

	this.publishMap = function (repositoryType) {
		var repository = chooseRepository([repositoryType, mapInfo.mapId]),
			mapSaved = function (savedMapInfo) {
				dispatchEvent('mapSaved', savedMapInfo.mapId, savedMapInfo.idea, (mapInfo.mapId !== savedMapInfo.mapId));
				mapInfo = savedMapInfo;
			},
			progressEvent = function (evt) {
				var done = (evt && evt.loaded) || 0,
					total = (evt && evt.total) || 1,
					message = ((evt && evt.loaded) ? Math.round(100 * done / total) + "%" : evt);
				dispatchEvent('mapSaving', repository.description, message);
			},
			mapSaveFailed = function (reason, label) {
				var retryWithDialog = function () {
					dispatchEvent('mapSaving', repository.description);
					repository.saveMap(_.clone(mapInfo), true).then(mapSaved, mapSaveFailed).progress(progressEvent);
				}, repositoryName = repository.description || '';
				label = label ? label + repositoryName : repositoryName;
				if (reason === 'no-access-allowed') {
					dispatchEvent('mapSavingUnAuthorized', function () {
						dispatchEvent('mapSaving', repository.description, 'Creating a new file');
						var saveAsNewInfo = _.clone(mapInfo);
						saveAsNewInfo.mapId = 'new';
						repository.saveMap(saveAsNewInfo, true).then(mapSaved, mapSaveFailed).progress(progressEvent);
					});
				} else if (reason === 'failed-authentication') {
					dispatchEvent('authorisationFailed', label, retryWithDialog);
				} else if (reason === 'not-authenticated') {
					dispatchEvent('authRequired', label, retryWithDialog);
				} else {
					dispatchEvent('mapSavingFailed', reason, label);
				}
			};
		dispatchEvent('mapSaving', repository.description);
		MM.retry(repository.saveMap.bind(repository, _.clone(mapInfo)), shouldRetry(5), MM.linearBackoff()).then(mapSaved, mapSaveFailed).progress(progressEvent);
	};
};

MM.MapRepository.activityTracking = function (mapRepository, activityLog) {
	'use strict';
	var startedFromNew = function (idea) {
		return idea.id === 1;
	},
		isNodeRelevant = function (ideaNode) {
			return ideaNode.title && ideaNode.title.search(/MindMup|Lancelot|cunning|brilliant|Press Space|famous|Luke|daddy/) === -1;
		},
		isNodeIrrelevant = function (ideaNode) {
			return !isNodeRelevant(ideaNode);
		},
		isMapRelevant = function (idea) {
			return startedFromNew(idea) && idea.find(isNodeRelevant).length > 5 && idea.find(isNodeIrrelevant).length < 3;
		},
		wasRelevantOnLoad;
	mapRepository.addEventListener('mapLoading', function (mapUrl, percentDone) {
		activityLog.log('loading map [' + mapUrl + '] (' + percentDone + '%)');
	});
	mapRepository.addEventListener('mapLoaded', function (idea, mapId) {
		activityLog.log('Map', 'View', mapId);
		wasRelevantOnLoad = isMapRelevant(idea);
	});
	mapRepository.addEventListener('mapLoadingFailed', function (mapUrl, reason, label) {
		var message = 'Error loading map document [' + mapUrl + '] ' + JSON.stringify(reason);
		if (label) {
			message = message + ' label [' + label + ']';
		}
		activityLog.error(message);
	});
	mapRepository.addEventListener('mapSaving', activityLog.log.bind(activityLog, 'Map', 'Save Attempted'));
	mapRepository.addEventListener('mapSaved', function (id, idea) {
		if (isMapRelevant(idea) && !wasRelevantOnLoad) {
			activityLog.log('Map', 'Created Relevant', id);
		} else if (wasRelevantOnLoad) {
			activityLog.log('Map', 'Saved Relevant', id);
		} else {
			activityLog.log('Map', 'Saved Irrelevant', id);
		}
	});
	mapRepository.addEventListener('mapSavingFailed', function (reason, repositoryName) {
		activityLog.error('Map save failed (' + repositoryName + ')' + JSON.stringify(reason));
	});
	mapRepository.addEventListener('networkError', function (reason) {
		activityLog.log('Map', 'networkError', JSON.stringify(reason));
	});
};
MM.MapRepository.alerts = function (mapRepository, alert) {
	'use strict';
	var alertId,
		showAlertWithCallBack = function (message, prompt, type, callback) {
			alert.hide(alertId);
			alertId = alert.show(
				message,
				'' + prompt + '',
				type
			);
			jQuery('[data-mm-role=auth]').click(function () {
				alert.hide(alertId);
				callback();
			});
		},
		showErrorAlert = function (title, message) {
			alert.hide(alertId);
			alertId = alert.show(title, message, 'error');
		};
	mapRepository.addEventListener('mapLoading', function (mapUrl, progressMessage) {
		alert.hide(alertId);
		alertId = alert.show(' Please wait, loading the map...', (progressMessage || ''));
	});
	mapRepository.addEventListener('mapSaving', function (repositoryName, progressMessage) {
		alert.hide(alertId);
		alertId = alert.show(' Please wait, saving the map...', (progressMessage || ''));
	});
	mapRepository.addEventListener('authRequired', function (providerName, authCallback) {
		showAlertWithCallBack(
			'This operation requires authentication through ' + providerName + ' !',
			'Click here to authenticate',
			'warning',
			authCallback
		);
	});
	mapRepository.addEventListener('mapLoaded', function () {
		alert.hide(alertId);
	});
	mapRepository.addEventListener('authorisationFailed', function (providerName, authCallback) {
		showAlertWithCallBack(
			'We were unable to authenticate with ' + providerName,
			'Click here to try again',
			'warning',
			authCallback
		);
	});
	mapRepository.addEventListener('mapLoadingUnAuthorized', function () {
		showErrorAlert('The map could not be loaded.', 'You do not have the right to view this map');
	});
	mapRepository.addEventListener('mapSavingUnAuthorized', function (callback) {
		showAlertWithCallBack(
			'You do not have the right to edit this map',
			'Click here to save a copy',
			'error',
			callback
		);
	});
	mapRepository.addEventListener('mapLoadingFailed', function () {
		showErrorAlert('Unfortunately, there was a problem loading the map.', 'An automated error report was sent and we will look into this as soon as possible');
	});
	mapRepository.addEventListener('mapSavingFailed', function (reason) {
		var messages = {
			'file-too-large': ['Unfortunately, the file is too large for the selected storage provider.', 'Please select a different storage provider from the save dropdown menu'],
			'network-error': ['There was a problem communicating with the server.', 'Please try again later, or select a different storage provider from the save dropdown menu']
		},
			message = messages[reason] || ['Unfortunately, there was a problem saving the map.', 'Please try again later. We have sent an error report and we will look into this as soon as possible'];

		showErrorAlert(message[0], message[1]);
	});
};
MM.MapRepository.toolbarAndUnsavedChangesDialogue = function (mapRepository, activityLog) {
	'use strict';
	var changed, saving, mapLoaded,
		toggleChange = function () {
			saving = false;
			if (!changed) {
				jQuery('body').removeClass('map-unchanged').addClass('map-changed');
				activityLog.log('Map', 'Edit');
				changed = true;
			}
		};
	mapRepository.addEventListener('mapLoaded', function (idea, mapId) {
		jQuery('body').removeClass('map-changed').addClass('map-unchanged');
		changed = false;
		if (!mapLoaded) {
			jQuery(window).bind('beforeunload', function () {
				if (changed && !saving) {
					return 'There are unsaved changes.';
				}
			});
			mapLoaded = true;
		} else {
			toggleChange();
		}
		if (!mapId || mapId.length < 3) { /* imported, no repository ID */
			toggleChange();
		}
		idea.addEventListener('changed', function (command, args) {
			toggleChange();
			activityLog.log(['Map', command].concat(args));
		});
	});
	mapRepository.addEventListener('mapSaving', function () {
		saving = true;
	});
	mapRepository.addEventListener('mapSaved', function () {
		saving = false;
		changed = false;
		jQuery('body').removeClass('map-changed').addClass('map-unchanged');
	});
};
MM.MapRepository.mapLocationChange = function (mapRepository) {
	'use strict';
	mapRepository.addEventListener('mapSaved', function (newMapId, idea, idHasChanged) {
		if (idHasChanged) {
			document.location = '/map/' + newMapId;
		}
	});
};

MM.retry = function (task, shouldRetry, backoff) {
	'use strict';
	var deferred = jQuery.Deferred(),
		attemptTask = function () {
			task().then(
				deferred.resolve,
				function () {
					if (!shouldRetry || shouldRetry.apply(undefined, arguments)) {
						if (backoff) {
							setTimeout(attemptTask, backoff());
						} else {
							attemptTask();
						}
					} else {
						deferred.reject.apply(deferred, arguments);
					}
				}
			).progress(
				deferred.notify
			);
		};
	attemptTask();
	return deferred.promise();
};
MM.retryTimes = function (retries) {
	'use strict';
	return function () {
		return retries--;
	};
};
MM.linearBackoff = function () {
	'use strict';
	var calls = 0;
	return function () {
		calls++;
		return 1000 * calls;
	};
};

(function ($, window) {
	'use strict';
    //patch ajax settings to call a progress callback
    var oldXHR = $.ajaxSettings.xhr;
    $.ajaxSettings.xhr = function () {
        var xhr = oldXHR();
        if (xhr instanceof window.XMLHttpRequest) {
            xhr.addEventListener('progress', this.progress, false);
        }
        if (xhr.upload) {
            xhr.upload.addEventListener('progress', this.progress, false);
        }
        return xhr;
    };
}(jQuery, window));
<commit_msg>removing saving alerts after a file is saved<commit_after>/*global _, content, jQuery, MM, observable, window, document, setTimeout*/
MM.MapRepository = function (activityLog, alert, repositories) {
	// order of repositories is important, the first repository is default
	'use strict';
	observable(this);
	var dispatchEvent = this.dispatchEvent,
		mapInfo = {},
		chooseRepository = function (identifiers) {
			// order of identifiers is important, the first identifier takes precedence
			var idIndex, repoIndex;
			for (idIndex = 0; idIndex < identifiers.length; idIndex++) {
				for (repoIndex = 0; repoIndex < repositories.length; repoIndex++) {
					if (repositories[repoIndex].recognises(identifiers[idIndex])) {
						return repositories[repoIndex];
					}
				}
			}
			return repositories[0];
		},
		setMap = function (idea, mapId) {
			mapInfo = {
				idea: idea,
				mapId: mapId
			};
			dispatchEvent('mapLoaded', idea, mapId);
		},
		mapLoaded = function (fileContent, mapId, mimeType) {
			var json, idea;
			if (mimeType === 'application/json') {
				json = typeof fileContent === 'string' ? JSON.parse(fileContent) : fileContent;
			} else if (mimeType === 'application/octet-stream') {
				json = JSON.parse(fileContent);
			} else if (mimeType === 'application/x-freemind') {
				json = MM.freemindImport(fileContent);
			}
			idea = content(json);
			setMap(idea, mapId);
		},
		shouldRetry = function (retries) {
			var times = MM.retryTimes(retries);
			return function (status) {
				return times() && status === 'network-error';
			};
		};
	MM.MapRepository.mapLocationChange(this);
	MM.MapRepository.activityTracking(this, activityLog);

	MM.MapRepository.alerts(this, alert);
	MM.MapRepository.toolbarAndUnsavedChangesDialogue(this, activityLog);

	this.setMap = setMap;

	this.loadMap = function (mapId) {
		var timeout,
			repository = chooseRepository([mapId]),
			progressEvent = function (evt) {
				var done = (evt && evt.loaded) || 0,
					total = (evt && evt.total) || 1,
					message = ((evt && evt.loaded) ? Math.round(100 * done / total) + "%" : evt);
				dispatchEvent('mapLoading', mapId, message);
			},
			mapLoadFailed = function (reason, label) {
				var retryWithDialog = function () {
					dispatchEvent('mapLoading', mapId);
					repository.loadMap(mapId, true).then(mapLoaded, mapLoadFailed).progress(progressEvent);
				}, repositoryName = repository.description ? ' [' + repository.description + ']' : '';
				label = label ? label + repositoryName : repositoryName;
				if (reason === 'no-access-allowed') {
					dispatchEvent('mapLoadingUnAuthorized', mapId, reason);
				} else if (reason === 'failed-authentication') {
					dispatchEvent('authorisationFailed', repository.description, retryWithDialog);
				} else if (reason === 'not-authenticated') {
					dispatchEvent('authRequired', repository.description, retryWithDialog);
				} else {
					dispatchEvent('mapLoadingFailed', mapId, reason, label);
				}
			};
		dispatchEvent('mapLoading', mapId);
		MM.retry(
			repository.loadMap.bind(repository, mapId),
			shouldRetry(5),
			MM.linearBackoff()
		).then(
			mapLoaded,
			mapLoadFailed
		).progress(progressEvent);
	};

	this.publishMap = function (repositoryType) {
		var repository = chooseRepository([repositoryType, mapInfo.mapId]),
			mapSaved = function (savedMapInfo) {
				dispatchEvent('mapSaved', savedMapInfo.mapId, savedMapInfo.idea, (mapInfo.mapId !== savedMapInfo.mapId));
				mapInfo = savedMapInfo;
			},
			progressEvent = function (evt) {
				var done = (evt && evt.loaded) || 0,
					total = (evt && evt.total) || 1,
					message = ((evt && evt.loaded) ? Math.round(100 * done / total) + "%" : evt);
				dispatchEvent('mapSaving', repository.description, message);
			},
			mapSaveFailed = function (reason, label) {
				var retryWithDialog = function () {
					dispatchEvent('mapSaving', repository.description);
					repository.saveMap(_.clone(mapInfo), true).then(mapSaved, mapSaveFailed).progress(progressEvent);
				}, repositoryName = repository.description || '';
				label = label ? label + repositoryName : repositoryName;
				if (reason === 'no-access-allowed') {
					dispatchEvent('mapSavingUnAuthorized', function () {
						dispatchEvent('mapSaving', repository.description, 'Creating a new file');
						var saveAsNewInfo = _.clone(mapInfo);
						saveAsNewInfo.mapId = 'new';
						repository.saveMap(saveAsNewInfo, true).then(mapSaved, mapSaveFailed).progress(progressEvent);
					});
				} else if (reason === 'failed-authentication') {
					dispatchEvent('authorisationFailed', label, retryWithDialog);
				} else if (reason === 'not-authenticated') {
					dispatchEvent('authRequired', label, retryWithDialog);
				} else {
					dispatchEvent('mapSavingFailed', reason, label);
				}
			};
		dispatchEvent('mapSaving', repository.description);
		MM.retry(repository.saveMap.bind(repository, _.clone(mapInfo)), shouldRetry(5), MM.linearBackoff()).then(mapSaved, mapSaveFailed).progress(progressEvent);
	};
};

MM.MapRepository.activityTracking = function (mapRepository, activityLog) {
	'use strict';
	var startedFromNew = function (idea) {
		return idea.id === 1;
	},
		isNodeRelevant = function (ideaNode) {
			return ideaNode.title && ideaNode.title.search(/MindMup|Lancelot|cunning|brilliant|Press Space|famous|Luke|daddy/) === -1;
		},
		isNodeIrrelevant = function (ideaNode) {
			return !isNodeRelevant(ideaNode);
		},
		isMapRelevant = function (idea) {
			return startedFromNew(idea) && idea.find(isNodeRelevant).length > 5 && idea.find(isNodeIrrelevant).length < 3;
		},
		wasRelevantOnLoad;
	mapRepository.addEventListener('mapLoading', function (mapUrl, percentDone) {
		activityLog.log('loading map [' + mapUrl + '] (' + percentDone + '%)');
	});
	mapRepository.addEventListener('mapLoaded', function (idea, mapId) {
		activityLog.log('Map', 'View', mapId);
		wasRelevantOnLoad = isMapRelevant(idea);
	});
	mapRepository.addEventListener('mapLoadingFailed', function (mapUrl, reason, label) {
		var message = 'Error loading map document [' + mapUrl + '] ' + JSON.stringify(reason);
		if (label) {
			message = message + ' label [' + label + ']';
		}
		activityLog.error(message);
	});
	mapRepository.addEventListener('mapSaving', activityLog.log.bind(activityLog, 'Map', 'Save Attempted'));
	mapRepository.addEventListener('mapSaved', function (id, idea) {
		if (isMapRelevant(idea) && !wasRelevantOnLoad) {
			activityLog.log('Map', 'Created Relevant', id);
		} else if (wasRelevantOnLoad) {
			activityLog.log('Map', 'Saved Relevant', id);
		} else {
			activityLog.log('Map', 'Saved Irrelevant', id);
		}
	});
	mapRepository.addEventListener('mapSavingFailed', function (reason, repositoryName) {
		activityLog.error('Map save failed (' + repositoryName + ')' + JSON.stringify(reason));
	});
	mapRepository.addEventListener('networkError', function (reason) {
		activityLog.log('Map', 'networkError', JSON.stringify(reason));
	});
};
MM.MapRepository.alerts = function (mapRepository, alert) {
	'use strict';
	var alertId,
		showAlertWithCallBack = function (message, prompt, type, callback) {
			alert.hide(alertId);
			alertId = alert.show(
				message,
				'' + prompt + '',
				type
			);
			jQuery('[data-mm-role=auth]').click(function () {
				alert.hide(alertId);
				callback();
			});
		},
		showErrorAlert = function (title, message) {
			alert.hide(alertId);
			alertId = alert.show(title, message, 'error');
		};
	mapRepository.addEventListener('mapLoading', function (mapUrl, progressMessage) {
		alert.hide(alertId);
		alertId = alert.show(' Please wait, loading the map...', (progressMessage || ''));
	});
	mapRepository.addEventListener('mapSaving', function (repositoryName, progressMessage) {
		alert.hide(alertId);
		alertId = alert.show(' Please wait, saving the map...', (progressMessage || ''));
	});
	mapRepository.addEventListener('authRequired', function (providerName, authCallback) {
		showAlertWithCallBack(
			'This operation requires authentication through ' + providerName + ' !',
			'Click here to authenticate',
			'warning',
			authCallback
		);
	});
	mapRepository.addEventListener('mapSaved', function () {
		alert.hide(alertId);
	});
	mapRepository.addEventListener('mapLoaded', function () {
		alert.hide(alertId);
	});
	mapRepository.addEventListener('authorisationFailed', function (providerName, authCallback) {
		showAlertWithCallBack(
			'We were unable to authenticate with ' + providerName,
			'Click here to try again',
			'warning',
			authCallback
		);
	});
	mapRepository.addEventListener('mapLoadingUnAuthorized', function () {
		showErrorAlert('The map could not be loaded.', 'You do not have the right to view this map');
	});
	mapRepository.addEventListener('mapSavingUnAuthorized', function (callback) {
		showAlertWithCallBack(
			'You do not have the right to edit this map',
			'Click here to save a copy',
			'error',
			callback
		);
	});
	mapRepository.addEventListener('mapLoadingFailed', function () {
		showErrorAlert('Unfortunately, there was a problem loading the map.', 'An automated error report was sent and we will look into this as soon as possible');
	});
	mapRepository.addEventListener('mapSavingFailed', function (reason) {
		var messages = {
			'file-too-large': ['Unfortunately, the file is too large for the selected storage provider.', 'Please select a different storage provider from the save dropdown menu'],
			'network-error': ['There was a problem communicating with the server.', 'Please try again later, or select a different storage provider from the save dropdown menu']
		},
			message = messages[reason] || ['Unfortunately, there was a problem saving the map.', 'Please try again later. We have sent an error report and we will look into this as soon as possible'];

		showErrorAlert(message[0], message[1]);
	});
};
MM.MapRepository.toolbarAndUnsavedChangesDialogue = function (mapRepository, activityLog) {
	'use strict';
	var changed, saving, mapLoaded,
		toggleChange = function () {
			saving = false;
			if (!changed) {
				jQuery('body').removeClass('map-unchanged').addClass('map-changed');
				activityLog.log('Map', 'Edit');
				changed = true;
			}
		};
	mapRepository.addEventListener('mapLoaded', function (idea, mapId) {
		jQuery('body').removeClass('map-changed').addClass('map-unchanged');
		changed = false;
		if (!mapLoaded) {
			jQuery(window).bind('beforeunload', function () {
				if (changed && !saving) {
					return 'There are unsaved changes.';
				}
			});
			mapLoaded = true;
		} else {
			toggleChange();
		}
		if (!mapId || mapId.length < 3) { /* imported, no repository ID */
			toggleChange();
		}
		idea.addEventListener('changed', function (command, args) {
			toggleChange();
			activityLog.log(['Map', command].concat(args));
		});
	});
	mapRepository.addEventListener('mapSaving', function () {
		saving = true;
	});
	mapRepository.addEventListener('mapSaved', function () {
		saving = false;
		changed = false;
		jQuery('body').removeClass('map-changed').addClass('map-unchanged');
	});
};
MM.MapRepository.mapLocationChange = function (mapRepository) {
	'use strict';
	mapRepository.addEventListener('mapSaved', function (newMapId, idea, idHasChanged) {
		if (idHasChanged) {
			document.location = '/map/' + newMapId;
		}
	});
};

MM.retry = function (task, shouldRetry, backoff) {
	'use strict';
	var deferred = jQuery.Deferred(),
		attemptTask = function () {
			task().then(
				deferred.resolve,
				function () {
					if (!shouldRetry || shouldRetry.apply(undefined, arguments)) {
						if (backoff) {
							setTimeout(attemptTask, backoff());
						} else {
							attemptTask();
						}
					} else {
						deferred.reject.apply(deferred, arguments);
					}
				}
			).progress(
				deferred.notify
			);
		};
	attemptTask();
	return deferred.promise();
};
MM.retryTimes = function (retries) {
	'use strict';
	return function () {
		return retries--;
	};
};
MM.linearBackoff = function () {
	'use strict';
	var calls = 0;
	return function () {
		calls++;
		return 1000 * calls;
	};
};

(function ($, window) {
	'use strict';
    //patch ajax settings to call a progress callback
    var oldXHR = $.ajaxSettings.xhr;
    $.ajaxSettings.xhr = function () {
        var xhr = oldXHR();
        if (xhr instanceof window.XMLHttpRequest) {
            xhr.addEventListener('progress', this.progress, false);
        }
        if (xhr.upload) {
            xhr.upload.addEventListener('progress', this.progress, false);
        }
        return xhr;
    };
}(jQuery, window));
<|endoftext|>


<commit_before>/*

	tdapp.js

*/
var tdapp = angular.module("tdapp",['satellizer']);

tdapp.config(function($authProvider) {
	$authProvider.baseUrl='/api/todos/';
	$authProvider.loginUrl='login';

	// Satellizer
	$authProvider.twitter({
		url: 'http://127.0.0.1:5000/auth/twitter', // Satellizer server component on localhost
		authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate',
		redirectUri: window.location.origin,
		type: '1.0',
		popupOptions: { width: 495, height: 645 }
	});
	$authProvider.httpInterceptor = false,
	$authProvider.withCredentials = false;
})

/*
  Server ----------------------------------------
*/
var localserver = "http://localhost:3000/api/todos"; // JSON-Server ressource on localhost
var server = window.location.protocol + "/api/todos";

/*
  Factory ----------------------------------------
*/
tdapp.factory("TDMgr",function(){

	// Logging

	var log = [];

	function _log(logtxt){
		log.push(logtxt);
		if(log.length>32){
			log.shift();
		}
	}

	function _getLog(){
		return log;
	}

	for(i=0;i<32;i++){
		_log("");
	}

	// Todos

	var todos = [];

	function _getTodos(){
		return todos;
	}

	function _setTodos(todolist){
		todos = todolist;
	}

	function _clearTodos(){
		while(todos.length>0){
			todos.pop();
		}
	}

	function _getTodoById(id){
		var ret = undefined;
		todos.forEach(function(obj){
			if(obj.id==id){
				ret = obj;
			}
		});
		return ret;
	}

	function _addTodoObj(obj){
		todos.unshift(obj);
	}

	function _delTodo(item){
		var idx = todos.indexOf(item)
		todos.splice(idx,1);
	}

	function _togDone(item){
		var idx = todos.indexOf(item);
		var todo = todos[idx];
		if(todo.done){
			todo.done = false;
		} else {
			todo.done = true;
		}
	}

	// Returns

	return{
		log : _log,
		getLog : _getLog,
		getTodos : _getTodos,
		setTodos : _setTodos,
		clearTodos : _clearTodos,
		getTodoById : _getTodoById,
		addTodoObj : _addTodoObj,
		delTodo : _delTodo,
		togDone : _togDone,
	};

});

/*
  Main controller ----------------------------------------
*/
tdapp.controller("MainCtrl",function($scope,$timeout,$interval,$http,$auth,TDMgr){

	// Init

	$scope.todos = TDMgr.getTodos();
	$scope.log = TDMgr.getLog();

	$scope.s_login = 1;
	$scope.s_working = 0;
	$scope.s_list = 0;
	$scope.s_add = 0;

	// Communication with server

	function errorCallback(response) {
		TDMgr.log("Error!");
		TDMgr.log("Check browser console for details.");
		console.log(JSON.stringify(response));
	}

	function gettodos(){
		TDMgr.log("Sending request (get) to server...");
		$scope.s_working = 1;
		$scope.s_list = 0;
		$http({
			method:"get",
			url: server
		}).then(
			function successCallback(response) {
				TDMgr.log("Done.");
				TDMgr.log("Updating local Todo-List.");
				response.data.forEach(function(o){
					TDMgr.addTodoObj(o);
				});
				TDMgr.log("Done.");
				$timeout(function(){
					$scope.s_list = 1;
					$scope.s_working = 0;
				},1000);
				$timeout(function(){
					document.getElementById("todotxtaconst").focus();
				},1128);
			}
			,
			function(response) {
				TDMgr.log("Error!");
				TDMgr.log("Check browser console for details.");
				console.log(JSON.stringify(response));
				$scope.errormsg="Server not available!";
				$scope.dologout();
			}
		);
	}
	$scope.gettodos = gettodos;

	function posttodo(obj){
		TDMgr.log("Sending request (post) to server...");
		$http({
			method:"post",
			url: server,
			header: "application/json",
			data: obj
		}).then(
			function successCallback(response) {
				TDMgr.log("Done.");
				obj.id=response.data.id;
			}
			,
			errorCallback
		);
	}

	function puttodo(obj){
		TDMgr.log("Sending request (put) to server...");
		$http({
			method:"put",
			url: server+"/"+obj.id,
			header: "application/json",
			data: obj
		}).then(
			function successCallback(response) {
				TDMgr.log("Done.");
			}
			,
			errorCallback
		);
	}

	function deletetodo(obj){
		TDMgr.log("Sending request (delete) to server...");
		$http({
			method:"delete",
			url: server+"/"+obj.id,
			header: "application/json",
			data: obj
		}).then(
			function successCallback(response) {
				TDMgr.log("Done.");
			}
			,
			errorCallback
		);
	}

	function donetodo(obj){
		TDMgr.log("Sending request (get; todo is done) to server...");
		$http({
			method:"get",
			url: server+"/"+obj.id+"/done",
		}).then(
			function successCallback(response) {
				TDMgr.log("Done.");
			}
			,
			errorCallback
		);
	}

	// Satellizer

	$scope.authenticate = function(provider){
		$auth.authenticate(provider)
		.then(function(res){
			if($auth.isAuthenticated){
				var dtok = jwt_decode(res.data.token);
				$scope.user = {};
				$scope.user.id = dtok.sub;
			}
		});
	};

	// Keyboard functions

	$scope.mainKeydown = function(e){
		if($scope.s_login==0){
			var k = e.keyCode;
			if(k==107){//+ on numpad
				e.preventDefault();
				$scope.s_add = 1;
				$timeout(function(){
					document.getElementById("todotxta").focus();
				},128);
				TDMgr.log("NewTodo-Dialog.");
			}
		}
	}

	$scope.newtodoKeydown = function(e){
		var k = e.keyCode;
		if(k==13){//ret
			e.preventDefault();
			if($scope.newtodotxt!=""){
				var newtodo = {};
				newtodo.topic=$scope.newtodotxt;
				newtodo.done=false;
				$scope.newtodotxt = "";
				posttodo(newtodo);
				TDMgr.addTodoObj(newtodo);
				window.scrollTo(0,0);
				document.getElementById("todotxtaconst").focus();
				$scope.s_add = 0;
			}
		} else
		if(k==27){//esc
			e.preventDefault();
			$scope.newtodotxt = "";
			$scope.s_add = 0;
			$scope.s_list = 1;
			TDMgr.log("Todo not added.");
			document.getElementById("todotxtaconst").focus();
		} else
		if(k==9){//tab
			e.preventDefault();
		}
	}

	$scope.todolineKeydown = function(e,id){
		var k = e.keyCode;
		if(k==13 || k==27 || k==107){//ret,esc,+
			e.preventDefault();
			TDMgr.log("Change Todo (id:"+id+").");
			var currentTodo = document.getElementById("todoid"+id);
			currentTodo.blur();
			TDMgr.log("Todo unfocused.");

			var obj = TDMgr.getTodoById(id);
			if(obj!=undefined){
				TDMgr.log("Done.");
				TDMgr.log("Updating data on server.");
				obj.topic = currentTodo.innerHTML;
				puttodo(obj);
				TDMgr.log("Todo (id:"+id+") updated.");
			} else {
				TDMgr.log("Error.");
			}
		}
	}

	$scope.loginKeydown = function(e){
		var k = e.keyCode;
		if(k==13){//ret
			e.preventDefault();
			$scope.dologin();
		}
	}

	// Todo functions

	$scope.seltodoline = function(id){
		document.getElementById("todoid"+id).focus();
		TDMgr.log("Todo focused.");
	}

	$scope.deltodo = function(obj){
		//Just some animated testings (wip)
		obj.opac = 1;
		obj.del = false;
		$interval(function(){
			var x = document.getElementById("todox"+obj.id);
			obj.opac = obj.opac - 0.05;
			x.style.opacity = obj.opac;
			if(obj.opac<=0 && !obj.deleted){
				obj.deleted = true;
				deletetodo(obj);
				//TDMgr.delTodo(obj);
				TDMgr.log("ToDo deleted.");
			}
		},32,32);
		if(TDMgr.getTodos().length==0){
			TDMgr.log("Todo-List is empty.");
		}
	}

	$scope.togDone = function(obj){
		TDMgr.togDone(obj);
		donetodo(obj);
		TDMgr.log("Todo-Flag changed.");
	}

	$scope.newtodo = function(){
		$scope.s_add = 1;
		$timeout(function(){
			document.getElementById("todotxta").focus();
		},128);
		TDMgr.log("New Todo-Dialog.");
	}

	$scope.newtodofromconst = function(){
		if($scope.newtodotxt!=undefined){
			if($scope.newtodotxt!=""){
				var newtodo = {};
				newtodo.topic=$scope.newtodotxt;
				newtodo.done=false;
				$scope.newtodotxt = "";
				posttodo(newtodo);
				TDMgr.addTodoObj(newtodo);
				window.scrollTo(0,0);
			}
		}
	}

	// Login functions

	function logout(){
		$scope.s_login = 0;
		$scope.s_list = 0;
		$scope.s_add = 0;
		$scope.s_working = 1;
		TDMgr.clearTodos();
		$timeout(function(){
			$scope.s_working = 0;
			$scope.s_login = 1;
		},1000);
		$timeout(function(){
			document.getElementById("liusername").focus();
		},1128);
		TDMgr.log("Logged out.");
	}
	$scope.dologout = logout;

	function login(){
		$auth.login($scope.user)
			.then(function(){
				TDMgr.log('You have successfully signed in!');
				TDMgr.log("Logged in.");
				$scope.errormsg = "";
				$scope.s_login = 0;
				gettodos();
			})
			.catch(function(error){
				$scope.errormsg = "Login-Error.";
				$scope.dologout();
				// TDMgr.log(error.data.message);
			});
	}
	function locallogin(){ // No basic authentication (for communication with localhost)

		server = localserver;
		$scope.errormsg = "";
		$scope.s_login = 0;
		gettodos();
	}
	$scope.dologin = login; // Change to "locallogin" for working against localhost

	// Finish

	TDMgr.log("Client Log-System ready.");

	document.getElementById("liusername").focus();

	if($scope.s_list==1){
		$scope.gettodos();
	}

});
<commit_msg>Auskommentierung Satellizer Konfiguration<commit_after>/*

	tdapp.js

*/
var tdapp = angular.module("tdapp",['satellizer']);

tdapp.config(function($authProvider) {
	$authProvider.baseUrl='/api/todos/';
	$authProvider.loginUrl='login';

	// Satellizer
	$authProvider.twitter({
		url: 'http://127.0.0.1:5000/auth/twitter', // Satellizer server component on localhost
		authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate',
		redirectUri: window.location.origin,
		type: '1.0',
		popupOptions: { width: 495, height: 645 }
	});
	/*
	$authProvider.httpInterceptor = false,
	$authProvider.withCredentials = false;
	*/
})

/*
  Server ----------------------------------------
*/
var localserver = "http://localhost:3000/api/todos"; // JSON-Server ressource on localhost
var server = window.location.protocol + "/api/todos";

/*
  Factory ----------------------------------------
*/
tdapp.factory("TDMgr",function(){

	// Logging

	var log = [];

	function _log(logtxt){
		log.push(logtxt);
		if(log.length>32){
			log.shift();
		}
	}

	function _getLog(){
		return log;
	}

	for(i=0;i<32;i++){
		_log("");
	}

	// Todos

	var todos = [];

	function _getTodos(){
		return todos;
	}

	function _setTodos(todolist){
		todos = todolist;
	}

	function _clearTodos(){
		while(todos.length>0){
			todos.pop();
		}
	}

	function _getTodoById(id){
		var ret = undefined;
		todos.forEach(function(obj){
			if(obj.id==id){
				ret = obj;
			}
		});
		return ret;
	}

	function _addTodoObj(obj){
		todos.unshift(obj);
	}

	function _delTodo(item){
		var idx = todos.indexOf(item)
		todos.splice(idx,1);
	}

	function _togDone(item){
		var idx = todos.indexOf(item);
		var todo = todos[idx];
		if(todo.done){
			todo.done = false;
		} else {
			todo.done = true;
		}
	}

	// Returns

	return{
		log : _log,
		getLog : _getLog,
		getTodos : _getTodos,
		setTodos : _setTodos,
		clearTodos : _clearTodos,
		getTodoById : _getTodoById,
		addTodoObj : _addTodoObj,
		delTodo : _delTodo,
		togDone : _togDone,
	};

});

/*
  Main controller ----------------------------------------
*/
tdapp.controller("MainCtrl",function($scope,$timeout,$interval,$http,$auth,TDMgr){

	// Init

	$scope.todos = TDMgr.getTodos();
	$scope.log = TDMgr.getLog();

	$scope.s_login = 1;
	$scope.s_working = 0;
	$scope.s_list = 0;
	$scope.s_add = 0;

	// Communication with server

	function errorCallback(response) {
		TDMgr.log("Error!");
		TDMgr.log("Check browser console for details.");
		console.log(JSON.stringify(response));
	}

	function gettodos(){
		TDMgr.log("Sending request (get) to server...");
		$scope.s_working = 1;
		$scope.s_list = 0;
		$http({
			method:"get",
			url: server
		}).then(
			function successCallback(response) {
				TDMgr.log("Done.");
				TDMgr.log("Updating local Todo-List.");
				response.data.forEach(function(o){
					TDMgr.addTodoObj(o);
				});
				TDMgr.log("Done.");
				$timeout(function(){
					$scope.s_list = 1;
					$scope.s_working = 0;
				},1000);
				$timeout(function(){
					document.getElementById("todotxtaconst").focus();
				},1128);
			}
			,
			function(response) {
				TDMgr.log("Error!");
				TDMgr.log("Check browser console for details.");
				console.log(JSON.stringify(response));
				$scope.errormsg="Server not available!";
				$scope.dologout();
			}
		);
	}
	$scope.gettodos = gettodos;

	function posttodo(obj){
		TDMgr.log("Sending request (post) to server...");
		$http({
			method:"post",
			url: server,
			header: "application/json",
			data: obj
		}).then(
			function successCallback(response) {
				TDMgr.log("Done.");
				obj.id=response.data.id;
			}
			,
			errorCallback
		);
	}

	function puttodo(obj){
		TDMgr.log("Sending request (put) to server...");
		$http({
			method:"put",
			url: server+"/"+obj.id,
			header: "application/json",
			data: obj
		}).then(
			function successCallback(response) {
				TDMgr.log("Done.");
			}
			,
			errorCallback
		);
	}

	function deletetodo(obj){
		TDMgr.log("Sending request (delete) to server...");
		$http({
			method:"delete",
			url: server+"/"+obj.id,
			header: "application/json",
			data: obj
		}).then(
			function successCallback(response) {
				TDMgr.log("Done.");
			}
			,
			errorCallback
		);
	}

	function donetodo(obj){
		TDMgr.log("Sending request (get; todo is done) to server...");
		$http({
			method:"get",
			url: server+"/"+obj.id+"/done",
		}).then(
			function successCallback(response) {
				TDMgr.log("Done.");
			}
			,
			errorCallback
		);
	}

	// Satellizer

	$scope.authenticate = function(provider){
		$auth.authenticate(provider)
		.then(function(res){
			if($auth.isAuthenticated){
				var dtok = jwt_decode(res.data.token);
				$scope.user = {};
				$scope.user.id = dtok.sub;
			}
		});
	};

	// Keyboard functions

	$scope.mainKeydown = function(e){
		if($scope.s_login==0){
			var k = e.keyCode;
			if(k==107){//+ on numpad
				e.preventDefault();
				$scope.s_add = 1;
				$timeout(function(){
					document.getElementById("todotxta").focus();
				},128);
				TDMgr.log("NewTodo-Dialog.");
			}
		}
	}

	$scope.newtodoKeydown = function(e){
		var k = e.keyCode;
		if(k==13){//ret
			e.preventDefault();
			if($scope.newtodotxt!=""){
				var newtodo = {};
				newtodo.topic=$scope.newtodotxt;
				newtodo.done=false;
				$scope.newtodotxt = "";
				posttodo(newtodo);
				TDMgr.addTodoObj(newtodo);
				window.scrollTo(0,0);
				document.getElementById("todotxtaconst").focus();
				$scope.s_add = 0;
			}
		} else
		if(k==27){//esc
			e.preventDefault();
			$scope.newtodotxt = "";
			$scope.s_add = 0;
			$scope.s_list = 1;
			TDMgr.log("Todo not added.");
			document.getElementById("todotxtaconst").focus();
		} else
		if(k==9){//tab
			e.preventDefault();
		}
	}

	$scope.todolineKeydown = function(e,id){
		var k = e.keyCode;
		if(k==13 || k==27 || k==107){//ret,esc,+
			e.preventDefault();
			TDMgr.log("Change Todo (id:"+id+").");
			var currentTodo = document.getElementById("todoid"+id);
			currentTodo.blur();
			TDMgr.log("Todo unfocused.");

			var obj = TDMgr.getTodoById(id);
			if(obj!=undefined){
				TDMgr.log("Done.");
				TDMgr.log("Updating data on server.");
				obj.topic = currentTodo.innerHTML;
				puttodo(obj);
				TDMgr.log("Todo (id:"+id+") updated.");
			} else {
				TDMgr.log("Error.");
			}
		}
	}

	$scope.loginKeydown = function(e){
		var k = e.keyCode;
		if(k==13){//ret
			e.preventDefault();
			$scope.dologin();
		}
	}

	// Todo functions

	$scope.seltodoline = function(id){
		document.getElementById("todoid"+id).focus();
		TDMgr.log("Todo focused.");
	}

	$scope.deltodo = function(obj){
		//Just some animated testings (wip)
		obj.opac = 1;
		obj.del = false;
		$interval(function(){
			var x = document.getElementById("todox"+obj.id);
			obj.opac = obj.opac - 0.05;
			x.style.opacity = obj.opac;
			if(obj.opac<=0 && !obj.deleted){
				obj.deleted = true;
				deletetodo(obj);
				//TDMgr.delTodo(obj);
				TDMgr.log("ToDo deleted.");
			}
		},32,32);
		if(TDMgr.getTodos().length==0){
			TDMgr.log("Todo-List is empty.");
		}
	}

	$scope.togDone = function(obj){
		TDMgr.togDone(obj);
		donetodo(obj);
		TDMgr.log("Todo-Flag changed.");
	}

	$scope.newtodo = function(){
		$scope.s_add = 1;
		$timeout(function(){
			document.getElementById("todotxta").focus();
		},128);
		TDMgr.log("New Todo-Dialog.");
	}

	$scope.newtodofromconst = function(){
		if($scope.newtodotxt!=undefined){
			if($scope.newtodotxt!=""){
				var newtodo = {};
				newtodo.topic=$scope.newtodotxt;
				newtodo.done=false;
				$scope.newtodotxt = "";
				posttodo(newtodo);
				TDMgr.addTodoObj(newtodo);
				window.scrollTo(0,0);
			}
		}
	}

	// Login functions

	function logout(){
		$scope.s_login = 0;
		$scope.s_list = 0;
		$scope.s_add = 0;
		$scope.s_working = 1;
		TDMgr.clearTodos();
		$timeout(function(){
			$scope.s_working = 0;
			$scope.s_login = 1;
		},1000);
		$timeout(function(){
			document.getElementById("liusername").focus();
		},1128);
		TDMgr.log("Logged out.");
	}
	$scope.dologout = logout;

	function login(){
		$auth.login($scope.user)
			.then(function(){
				TDMgr.log('You have successfully signed in!');
				TDMgr.log("Logged in.");
				$scope.errormsg = "";
				$scope.s_login = 0;
				gettodos();
			})
			.catch(function(error){
				$scope.errormsg = "Login-Error.";
				$scope.dologout();
				// TDMgr.log(error.data.message);
			});
	}
	function locallogin(){ // No basic authentication (for communication with localhost)

		server = localserver;
		$scope.errormsg = "";
		$scope.s_login = 0;
		gettodos();
	}
	$scope.dologin = login; // Change to "locallogin" for working against localhost

	// Finish

	TDMgr.log("Client Log-System ready.");

	document.getElementById("liusername").focus();

	if($scope.s_list==1){
		$scope.gettodos();
	}

});
<|endoftext|>


<commit_before>/*jslint forin: true nomen: true, plusplus: true*/
/*global _, content, jQuery, MM, observable, setTimeout, window, document*/
MM.MapRepository = function (activityLog, alert, repositories) {
	// order of repositories is important, the first repository is default
	'use strict';
	observable(this);
	var dispatchEvent = this.dispatchEvent,
		mapInfo = {},
		listeners = {
			'Before Upload': function (id, idea) {
				dispatchEvent('Before Upload', id, idea);
			},
			'mapSaved': function (savedMapInfo) {
				dispatchEvent('mapSaved', savedMapInfo.mapId, savedMapInfo.idea);
				if (mapInfo.mapId !== savedMapInfo.mapId) {
					document.location = "/map/" + savedMapInfo.mapId;
				}
				mapInfo = savedMapInfo;
			},
			'authRequired': function (message, authCallback) {
				dispatchEvent('authRequired', message, authCallback);
			},
			'mapSavingFailed': function () {
				dispatchEvent('mapSavingFailed');
			},
			'mapSaving': function () {
				dispatchEvent('mapSaving');
			}
		},
		addListeners = function (repository) {
			var listenType;
			for (listenType in listeners) {
				repository.addEventListener(listenType, listeners[listenType]);
			}
		},
		chooseRepository = function (identifiers) {
			// order of identifiers is important, the first identifier takes precedence
			var idIndex, repoIndex;
			for (idIndex = 0; idIndex < identifiers.length; idIndex++) {
				for (repoIndex = 0; repoIndex < repositories.length; repoIndex++) {
					if (repositories[repoIndex].recognises(identifiers[idIndex])) {
						return repositories[repoIndex];
					}
				}
			}
			return repositories[0];
		};

	MM.MapRepository.activityTracking(this, activityLog);
	MM.MapRepository.alerts(this, alert);
	MM.MapRepository.toolbarAndUnsavedChangesDialogue(this, activityLog);
	_.each(repositories, addListeners);

	this.setMap = function (mapInfo) {
		listeners.mapLoaded(mapInfo);
	};

	this.loadMap = function (mapId) {
		var repository = chooseRepository([mapId]);
		dispatchEvent('mapLoading', mapId);
		repository.use(
			function () {
				repository.loadMap(mapId).fail(
					function (errorMessage) {
						dispatchEvent('mapLoadingFailed', mapId, errorMessage);
					}
				).done(function (newMapInfo) {
					mapInfo = _.clone(newMapInfo);
					dispatchEvent('mapLoaded', newMapInfo.idea, newMapInfo.mapId);
				});
			},
			function () {
				dispatchEvent('mapLoadingFailed', mapId);
			}
		);
	};

	this.publishMap = function (repositoryType) {
		var repository = chooseRepository([repositoryType, mapInfo.mapId]);
		repository.use(
			function () {
				repository.saveMap(_.clone(mapInfo));
			},
			function () {
				dispatchEvent('mapSavingFailed');
			}
		);
	};
};

MM.MapRepository.activityTracking = function (mapRepository, activityLog) {
	'use strict';
	var startedFromNew = function (idea) {
		return idea.id === 1;
	},
		isNodeRelevant = function (ideaNode) {
			return ideaNode.title && ideaNode.title.search(/MindMup|Lancelot|cunning|brilliant|Press Space|famous|Luke|daddy/) === -1;
		},
		isNodeIrrelevant = function (ideaNode) {
			return !isNodeRelevant(ideaNode);
		},
		isMapRelevant = function (idea) {
			return startedFromNew(idea) && idea.find(isNodeRelevant).length > 5 && idea.find(isNodeIrrelevant).length < 3;
		},
		wasRelevantOnLoad;
	mapRepository.addEventListener('mapLoading', function (mapUrl, mapId) {
		activityLog.log('loading map [' + mapUrl + ']');
	});
	mapRepository.addEventListener('mapLoaded', function (idea, mapId) {
		activityLog.log('Map', 'View', mapId);
		wasRelevantOnLoad = isMapRelevant(idea);
	});
	mapRepository.addEventListener('mapLoadingFailed', function (mapUrl, reason) {
		activityLog.error('Error loading map document [' + mapUrl + '] ' + reason);
	});
	mapRepository.addEventListener('Before Upload', function (id, idea) {
		if (isMapRelevant(idea) && !wasRelevantOnLoad) {
			activityLog.log('Map', 'Created Relevant', id);
		} else if (wasRelevantOnLoad) {
			activityLog.log('Map', 'Saved Relevant', id);
		} else {
			activityLog.log('Map', 'Saved Irrelevant', id);
		}
	});
	mapRepository.addEventListener('mapSavingFailed', function () {
		activityLog.error('Map save failed');
	});
};
MM.MapRepository.alerts = function (mapRepository, alert) {
	'use strict';
	var alertId;
	mapRepository.addEventListener('mapLoading', function () {
		alertId = alert.show('Please wait, loading the map...', '');
	});
	mapRepository.addEventListener('authRequired', function (message, authCallback) {
		alertId = alert.show(message, 'Click here to authenticate');
		jQuery('[data-mm-role=auth]').click(function () {
			alert.hide(alertId);
			authCallback();
		});
	});
	mapRepository.addEventListener('mapLoaded', function () {
		alert.hide(alertId);
	});
	mapRepository.addEventListener('mapLoadingFailed', function (mapUrl, reason) {
		alert.hide(alertId);
		alert.show(
			'Unfortunately, there was a problem loading the map.',
			'An automated error report was sent and we will look into this as soon as possible',
			'error'
		);
	});
	mapRepository.addEventListener('mapSavingFailed', function () {
		alert.show(
			'Unfortunately, there was a problem saving the map.',
			'Please try again later. We have sent an error report and we will look into this as soon as possible',
			'error'
		);
	});
};
MM.MapRepository.toolbarAndUnsavedChangesDialogue = function (mapRepository, activityLog) {
	'use strict';
	var changed, saving, mapLoaded,
		toggleChange = function () {
			saving = false;
			if (!changed) {
				jQuery('#toolbarShare').hide();
				jQuery('#toolbarSave').show();
				jQuery('#menuExport').hide();
				jQuery('#menuPublish').effect('highlight');
				activityLog.log('Map', 'Edit');
				changed = true;
			}
		};
	mapRepository.addEventListener('mapLoaded', function (idea) {
		if (!mapLoaded) {
			jQuery(window).bind('beforeunload', function () {
				if (changed && !saving) {
					return 'There are unsaved changes.';
				}
			});
			mapLoaded = true;
		} else {
			toggleChange();
		}
		idea.addEventListener('changed', function (command, args) {
			toggleChange();
			activityLog.log(['Map', command].concat(args));
		});
	});
	mapRepository.addEventListener('Before Upload', function () {
		saving = true;
	});
	mapRepository.addEventListener('mapSaving', function () {
		saving = true;
	});

	mapRepository.addEventListener('mapSaved', function () {
		saving = false;
		changed = false;
		jQuery('#toolbarShare').show();
		jQuery('#toolbarSave').hide();
		jQuery('#menuExport').show();
		jQuery('#menuPublish').hide();
	});
};
<commit_msg>map repository setMap should not use listener now<commit_after>/*jslint forin: true nomen: true, plusplus: true*/
/*global _, content, jQuery, MM, observable, setTimeout, window, document*/
MM.MapRepository = function (activityLog, alert, repositories) {
	// order of repositories is important, the first repository is default
	'use strict';
	observable(this);
	var dispatchEvent = this.dispatchEvent,
		mapInfo = {},
		listeners = {
			'Before Upload': function (id, idea) {
				dispatchEvent('Before Upload', id, idea);
			},
			'mapSaved': function (savedMapInfo) {
				dispatchEvent('mapSaved', savedMapInfo.mapId, savedMapInfo.idea);
				if (mapInfo.mapId !== savedMapInfo.mapId) {
					document.location = "/map/" + savedMapInfo.mapId;
				}
				mapInfo = savedMapInfo;
			},
			'authRequired': function (message, authCallback) {
				dispatchEvent('authRequired', message, authCallback);
			},
			'mapSavingFailed': function () {
				dispatchEvent('mapSavingFailed');
			},
			'mapSaving': function () {
				dispatchEvent('mapSaving');
			}
		},
		addListeners = function (repository) {
			var listenType;
			for (listenType in listeners) {
				repository.addEventListener(listenType, listeners[listenType]);
			}
		},
		chooseRepository = function (identifiers) {
			// order of identifiers is important, the first identifier takes precedence
			var idIndex, repoIndex;
			for (idIndex = 0; idIndex < identifiers.length; idIndex++) {
				for (repoIndex = 0; repoIndex < repositories.length; repoIndex++) {
					if (repositories[repoIndex].recognises(identifiers[idIndex])) {
						return repositories[repoIndex];
					}
				}
			}
			return repositories[0];
		},
		mapLoaded = function (newMapInfo) {
			mapInfo = _.clone(newMapInfo);
			dispatchEvent('mapLoaded', newMapInfo.idea, newMapInfo.mapId);
		};

	MM.MapRepository.activityTracking(this, activityLog);
	MM.MapRepository.alerts(this, alert);
	MM.MapRepository.toolbarAndUnsavedChangesDialogue(this, activityLog);
	_.each(repositories, addListeners);

	this.setMap = mapLoaded;

	this.loadMap = function (mapId) {
		var repository = chooseRepository([mapId]);
		dispatchEvent('mapLoading', mapId);
		repository.use(
			function () {
				repository.loadMap(mapId).fail(
					function (errorMessage) {
						dispatchEvent('mapLoadingFailed', mapId, errorMessage);
					}
				).done(mapLoaded);
			},
			function () {
				dispatchEvent('mapLoadingFailed', mapId);
			}
		);
	};

	this.publishMap = function (repositoryType) {
		var repository = chooseRepository([repositoryType, mapInfo.mapId]);
		repository.use(
			function () {
				repository.saveMap(_.clone(mapInfo));
			},
			function () {
				dispatchEvent('mapSavingFailed');
			}
		);
	};
};

MM.MapRepository.activityTracking = function (mapRepository, activityLog) {
	'use strict';
	var startedFromNew = function (idea) {
		return idea.id === 1;
	},
		isNodeRelevant = function (ideaNode) {
			return ideaNode.title && ideaNode.title.search(/MindMup|Lancelot|cunning|brilliant|Press Space|famous|Luke|daddy/) === -1;
		},
		isNodeIrrelevant = function (ideaNode) {
			return !isNodeRelevant(ideaNode);
		},
		isMapRelevant = function (idea) {
			return startedFromNew(idea) && idea.find(isNodeRelevant).length > 5 && idea.find(isNodeIrrelevant).length < 3;
		},
		wasRelevantOnLoad;
	mapRepository.addEventListener('mapLoading', function (mapUrl, mapId) {
		activityLog.log('loading map [' + mapUrl + ']');
	});
	mapRepository.addEventListener('mapLoaded', function (idea, mapId) {
		activityLog.log('Map', 'View', mapId);
		wasRelevantOnLoad = isMapRelevant(idea);
	});
	mapRepository.addEventListener('mapLoadingFailed', function (mapUrl, reason) {
		activityLog.error('Error loading map document [' + mapUrl + '] ' + reason);
	});
	mapRepository.addEventListener('Before Upload', function (id, idea) {
		if (isMapRelevant(idea) && !wasRelevantOnLoad) {
			activityLog.log('Map', 'Created Relevant', id);
		} else if (wasRelevantOnLoad) {
			activityLog.log('Map', 'Saved Relevant', id);
		} else {
			activityLog.log('Map', 'Saved Irrelevant', id);
		}
	});
	mapRepository.addEventListener('mapSavingFailed', function () {
		activityLog.error('Map save failed');
	});
};
MM.MapRepository.alerts = function (mapRepository, alert) {
	'use strict';
	var alertId;
	mapRepository.addEventListener('mapLoading', function () {
		alertId = alert.show('Please wait, loading the map...', '');
	});
	mapRepository.addEventListener('authRequired', function (message, authCallback) {
		alertId = alert.show(message, 'Click here to authenticate');
		jQuery('[data-mm-role=auth]').click(function () {
			alert.hide(alertId);
			authCallback();
		});
	});
	mapRepository.addEventListener('mapLoaded', function () {
		alert.hide(alertId);
	});
	mapRepository.addEventListener('mapLoadingFailed', function (mapUrl, reason) {
		alert.hide(alertId);
		alert.show(
			'Unfortunately, there was a problem loading the map.',
			'An automated error report was sent and we will look into this as soon as possible',
			'error'
		);
	});
	mapRepository.addEventListener('mapSavingFailed', function () {
		alert.show(
			'Unfortunately, there was a problem saving the map.',
			'Please try again later. We have sent an error report and we will look into this as soon as possible',
			'error'
		);
	});
};
MM.MapRepository.toolbarAndUnsavedChangesDialogue = function (mapRepository, activityLog) {
	'use strict';
	var changed, saving, mapLoaded,
		toggleChange = function () {
			saving = false;
			if (!changed) {
				jQuery('#toolbarShare').hide();
				jQuery('#toolbarSave').show();
				jQuery('#menuExport').hide();
				jQuery('#menuPublish').effect('highlight');
				activityLog.log('Map', 'Edit');
				changed = true;
			}
		};
	mapRepository.addEventListener('mapLoaded', function (idea) {
		if (!mapLoaded) {
			jQuery(window).bind('beforeunload', function () {
				if (changed && !saving) {
					return 'There are unsaved changes.';
				}
			});
			mapLoaded = true;
		} else {
			toggleChange();
		}
		idea.addEventListener('changed', function (command, args) {
			toggleChange();
			activityLog.log(['Map', command].concat(args));
		});
	});
	mapRepository.addEventListener('Before Upload', function () {
		saving = true;
	});
	mapRepository.addEventListener('mapSaving', function () {
		saving = true;
	});

	mapRepository.addEventListener('mapSaved', function () {
		saving = false;
		changed = false;
		jQuery('#toolbarShare').show();
		jQuery('#toolbarSave').hide();
		jQuery('#menuExport').show();
		jQuery('#menuPublish').hide();
	});
};
<|endoftext|>


<commit_before>/*
Language: Markdown
Author: John Crepezzi <john.crepezzi@gmail.com>
Website: http://seejohncode.com/
Description: Syntax Highlighting for markdown files
*/

hljs.LANGUAGES.markdown = function() {
  return {
    case_insensitive: true,
    defaultMode: {
      contains: [
        // highlight headers
        {
          className: 'header',
          begin: '^\#{1,3}',
          end: '$'
        },
        {
          className: 'header',
          begin: '^.+?\\n[=-]{2,}$',
          end: hljs.IMMEDIATE_RE
        },
        // inline html
        {
          begin: '<',
          end: '>',
          subLanguage: 'xml'
        },
        // lists (indicators only)
        {
          className: 'bullet',
          begin: '^([*+-]|(\\d+\.))\\s+',
          end: hljs.IMMEDIATE_RE
        },
        // strong segments
        {
          className: 'strong',
          begin: '[*_]{2}.+?[*_]{2}',
          end: hljs.IMMEDIATE_RE
        },
        // emphasis segments
        {
          className: 'emphasis',
          begin: '[*_].+?[*_]',
          end: hljs.IMMEDIATE_RE
        },
        // blockquotes
        {
          className: 'blockquote',
          begin: '^>\\s+',
          end: '$'
        },
        // code snippets
        {
          className: 'code',
          begin: '`.+?`',
          end: hljs.IMMEDIATE_RE
        },
        {
          className: 'code',
          begin: '^    ',
          end: '$',
          relevance: 0
        },
        // horizontal rules
        {
          className: 'horizontal_rule',
          begin: '^-{3,}',
          end: '$'
        },
        // using links - title and link
        {
          begin: '\\[.+?\\]\\(.+?\\)',
          end: hljs.IMMEDIATE_RE,
          returnBegin: true,
          contains: [
            {
              className: 'link_label',
              begin: '\\[.+\\]'
            },
            {
              className: 'link_url',
              begin: '\\(',
              end: '\\)'
            }
          ]
        }
      ]
    }
  };
}();
<commit_msg>Removed redundant things from markdown.js, reformatted code around 'ends' according to overall style.<commit_after>/*
Language: Markdown
Author: John Crepezzi <john.crepezzi@gmail.com>
Website: http://seejohncode.com/
*/

hljs.LANGUAGES.markdown = function() {
  return {
    case_insensitive: true,
    defaultMode: {
      contains: [
        // highlight headers
        {
          className: 'header',
          begin: '^\#{1,3}', end: '$'
        },
        {
          className: 'header',
          begin: '^.+?\\n[=-]{2,}$'
        },
        // inline html
        {
          begin: '<', end: '>',
          subLanguage: 'xml'
        },
        // lists (indicators only)
        {
          className: 'bullet',
          begin: '^([*+-]|(\\d+\.))\\s+'
        },
        // strong segments
        {
          className: 'strong',
          begin: '[*_]{2}.+?[*_]{2}'
        },
        // emphasis segments
        {
          className: 'emphasis',
          begin: '[*_].+?[*_]'
        },
        // blockquotes
        {
          className: 'blockquote',
          begin: '^>\\s+', end: '$'
        },
        // code snippets
        {
          className: 'code',
          begin: '`.+?`'
        },
        {
          className: 'code',
          begin: '^    ', end: '$',
          relevance: 0
        },
        // horizontal rules
        {
          className: 'horizontal_rule',
          begin: '^-{3,}', end: '$'
        },
        // using links - title and link
        {
          begin: '\\[.+?\\]\\(.+?\\)',
          returnBegin: true,
          contains: [
            {
              className: 'link_label',
              begin: '\\[.+\\]'
            },
            {
              className: 'link_url',
              begin: '\\(', end: '\\)'
            }
          ]
        }
      ]
    }
  };
}();
<|endoftext|>


<commit_before>var chai = require('chai')
var expect = chai.expect

var SessionStore = require('../session-store.js')
var TestManager = require('../test-manager.js')

describe('SessionStore#Disconnects', function() {

	it('after the MySQL connection is terminated, the SessionStore should attempt to re-establish the connection', function(done) {

		SessionStore.connection.destroy()
		SessionStore.connection.emit('error', {code: 'PROTOCOL_CONNECTION_LOST'})

		var elapsedTime = 0, intervalTime = 100

		var interval = setInterval(function() {

			elapsedTime += intervalTime

			if (elapsedTime > 1500)
			{
				clearInterval(interval)
				return done(new Error('Failed to re-establish connection after disconnect'))
			}

			if (SessionStore.connection.state == 'authenticated')
			{
				clearInterval(interval)
				done()
			}

		}, intervalTime)

	})

})<commit_msg>Lowered the time between each check for a re-established connection<commit_after>var chai = require('chai')
var expect = chai.expect

var SessionStore = require('../session-store.js')
var TestManager = require('../test-manager.js')

describe('SessionStore#Disconnects', function() {

	it('after the MySQL connection is terminated, the SessionStore should attempt to re-establish the connection', function(done) {

		SessionStore.connection.destroy()
		SessionStore.connection.emit('error', {code: 'PROTOCOL_CONNECTION_LOST'})

		var elapsedTime = 0, intervalTime = 50

		var interval = setInterval(function() {

			elapsedTime += intervalTime

			if (elapsedTime > 1500)
			{
				clearInterval(interval)
				return done(new Error('Failed to re-establish connection after disconnect'))
			}

			if (SessionStore.connection.state == 'authenticated')
			{
				clearInterval(interval)
				done()
			}

		}, intervalTime)

	})

})<|endoftext|>


<commit_before>

$(document).ready(function() {

	$(window).on('action:ajaxify.end', function(ev, data) {
		$('#users-container, .pending-friends').on('click', '.friend-button', addFriendFunctionality);
	});

	$(window).on('action:ajaxify.contentLoaded', function(ev, data) {
		if (data.url.match(/user\//)) {
			var uid = $('[data-uid]').attr('data-uid');

			socket.emit('plugins.friends.areFriendsOrRequested', {uid: uid}, function(err, isFriend) {
				if (isFriend[0]) {
					$('#follow-btn').after(' <button class="btn btn-link btn-sm friend-button" data-uid="' + uid + '" data-type="unfriend">Remove Friend</button>');
				} else {
					$('#follow-btn').after(' <button class="btn btn-warning btn-sm friend-button" data-uid="' + uid + '" data-type="friend">Add Friend</button>');
				}

				$('.friend-button').on('click', addFriendFunctionality);
			});
		}
	});

	function addFriendFunctionality() {
		var $this = $(this);
		friendCommand($this.attr('data-type'), $this.attr('data-uid'), $this);
	}

	function friendCommand(type, uid, btn) {
		socket.emit('plugins.friends.' + type, {
			uid: uid
		}, function(err) {
			if (err) {
				return app.alertError(err.message);
			}

			if (type === 'friend') {
				btn.attr('data-type', '').prop('disabled', true).html('Request Sent');
			} else if (type === 'unfriend') {
				btn.attr('data-type', 'friend')
					.addClass('btn-warning').removeClass('btn-link').html('Add Friend');
			} else if (type === 'accept') {
				ajaxify.go('user/' + app.user.userslug + '/friends');
			} else if (type === 'reject') {
				btn.parents('.registered-user').remove();
			}
		});
		return false;
	}
})<commit_msg>prevent double click events to trigger 2 accept friends<commit_after>

$(document).ready(function() {

	$(window).on('action:ajaxify.end', function(ev, data) {
		$('#users-container, .pending-friends').on('click', '.friend-button', addFriendFunctionality);
	});

	$(window).on('action:ajaxify.contentLoaded', function(ev, data) {
		if (data.tpl === 'account') {
			var uid = $('[data-uid]').attr('data-uid');

			socket.emit('plugins.friends.areFriendsOrRequested', {uid: uid}, function(err, isFriend) {
				if (isFriend[0]) {
					$('#follow-btn').after(' <button class="btn btn-link btn-sm friend-button" data-uid="' + uid + '" data-type="unfriend">Remove Friend</button>');
				} else {
					$('#follow-btn').after(' <button class="btn btn-warning btn-sm friend-button" data-uid="' + uid + '" data-type="friend">Add Friend</button>');
				}

				$('.friend-button').on('click', addFriendFunctionality);
			});
		}
	});

	function addFriendFunctionality() {
		var $this = $(this);
		friendCommand($this.attr('data-type'), $this.attr('data-uid'), $this);
	}

	function friendCommand(type, uid, btn) {
		socket.emit('plugins.friends.' + type, {
			uid: uid
		}, function(err) {
			if (err) {
				return app.alertError(err.message);
			}

			if (type === 'friend') {
				btn.attr('data-type', '').prop('disabled', true).html('Request Sent');
			} else if (type === 'unfriend') {
				btn.attr('data-type', 'friend')
					.addClass('btn-warning').removeClass('btn-link').html('Add Friend');
			} else if (type === 'accept') {
				ajaxify.go('user/' + app.user.userslug + '/friends');
			} else if (type === 'reject') {
				btn.parents('.registered-user').remove();
			}
		});
		return false;
	}
})<|endoftext|>


<commit_before>/*
Language: Markdown
Requires: xml.js
Author: John Crepezzi <john.crepezzi@gmail.com>
Website: http://seejohncode.com/
*/

function(hljs) {
  return {
    case_insensitive: true,
    contains: [
      // highlight headers
      {
        className: 'header',
        begin: '^#{1,3}', end: '$'
      },
      {
        className: 'header',
        begin: '^.+?\\n[=-]{2,}$'
      },
      // inline html
      {
        begin: '<', end: '>',
        subLanguage: 'xml',
        relevance: 0
      },
      // lists (indicators only)
      {
        className: 'bullet',
        begin: '^([*+-]|(\\d+\\.))\\s+'
      },
      // strong segments
      {
        className: 'strong',
        begin: '[*_]{2}.+?[*_]{2}'
      },
      // emphasis segments
      {
        className: 'emphasis',
        begin: '\\*.+?\\*'
      },
      {
        className: 'emphasis',
        begin: '_.+?_',
        relevance: 0
      },
      // blockquotes
      {
        className: 'blockquote',
        begin: '^>\\s+', end: '$'
      },
      // code snippets
      {
        className: 'code',
        begin: '`.+?`'
      },
      {
        className: 'code',
        begin: '^    ', end: '$',
        relevance: 0
      },
      // horizontal rules
      {
        className: 'horizontal_rule',
        begin: '^-{3,}', end: '$'
      },
      // using links - title and link
      {
        begin: '\\[.+?\\]\\(.+?\\)',
        returnBegin: true,
        contains: [
          {
            className: 'link_label',
            begin: '\\[.+\\]'
          },
          {
            className: 'link_url',
            begin: '\\(', end: '\\)',
            excludeBegin: true, excludeEnd: true
          }
        ]
      }
    ]
  };
}
<commit_msg>Case insensitivity doesn't make sense for Markdown<commit_after>/*
Language: Markdown
Requires: xml.js
Author: John Crepezzi <john.crepezzi@gmail.com>
Website: http://seejohncode.com/
*/

function(hljs) {
  return {
    contains: [
      // highlight headers
      {
        className: 'header',
        begin: '^#{1,3}', end: '$'
      },
      {
        className: 'header',
        begin: '^.+?\\n[=-]{2,}$'
      },
      // inline html
      {
        begin: '<', end: '>',
        subLanguage: 'xml',
        relevance: 0
      },
      // lists (indicators only)
      {
        className: 'bullet',
        begin: '^([*+-]|(\\d+\\.))\\s+'
      },
      // strong segments
      {
        className: 'strong',
        begin: '[*_]{2}.+?[*_]{2}'
      },
      // emphasis segments
      {
        className: 'emphasis',
        begin: '\\*.+?\\*'
      },
      {
        className: 'emphasis',
        begin: '_.+?_',
        relevance: 0
      },
      // blockquotes
      {
        className: 'blockquote',
        begin: '^>\\s+', end: '$'
      },
      // code snippets
      {
        className: 'code',
        begin: '`.+?`'
      },
      {
        className: 'code',
        begin: '^    ', end: '$',
        relevance: 0
      },
      // horizontal rules
      {
        className: 'horizontal_rule',
        begin: '^-{3,}', end: '$'
      },
      // using links - title and link
      {
        begin: '\\[.+?\\]\\(.+?\\)',
        returnBegin: true,
        contains: [
          {
            className: 'link_label',
            begin: '\\[.+\\]'
          },
          {
            className: 'link_url',
            begin: '\\(', end: '\\)',
            excludeBegin: true, excludeEnd: true
          }
        ]
      }
    ]
  };
}
<|endoftext|>


<commit_before>'use strict';

var _ = require('underscore');
var expect = require('chai').expect;

var ProxyLists = require('../../index');

describe('getProxies([options, ]cb)', function() {

	var originalDataSourcerClose;
	beforeEach(function() {
		originalDataSourcerClose = ProxyLists.DataSourcer.prototype.close;
	});

	afterEach(function() {
		ProxyLists.DataSourcer.prototype.close = originalDataSourcerClose;
		ProxyLists._sources = [];
	});

	it('should be a function', function() {
		expect(ProxyLists.getProxies).to.be.a('function');
	});

	it('should call getProxies() for all sources', function(done) {

		try {
			var testSources = ['somewhere', 'somewhere-else'];
			var called = {};

			_.each(testSources, function(name) {
				ProxyLists.addSource(name, {
					getProxies: function(options) {
						var emitter = options.newEventEmitter();
						_.defer(function() {
							called[name] = true;
							emitter.emit('end');
						});
						return emitter;
					}
				});
			});

			ProxyLists.getProxies({
				sourcesDir: null,
			}).on('end', function() {

				try {
					_.each(testSources, function(name) {
						expect(called[name]).to.equal(true);
					});
				} catch (error) {
					return done(error);
				}

				done();
			});
		} catch (error) {
			return done(error);
		}
	});

	it('source with ipv6 addresses', function(done) {

		ProxyLists.addSource('ipv6test', {
			getProxies: function(options) {
				var emitter = options.newEventEmitter();
				var onData = _.bind(emitter.emit, emitter, 'data');
				var onEnd = _.bind(emitter.emit, emitter, 'end');
				_.defer(onData, [{
					ipAddress: '1200:0000:AB00:1234:0000:2552:7777:1313',
					port: 80,
					anonymityLevel: 'transparent'
				}])
				_.defer(onEnd);
				return emitter;
			}
		});

		ProxyLists.getProxies({
			sourcesDir: null,
		}).on('end', function() {
			done();
		});
	});

	describe('options', function() {

		describe('series', function() {

			describe('TRUE', function() {

				it('should get proxies from all sources in series', function(done) {

					var testSources = ['somewhere', 'somewhere-else'];
					var called = {};

					var gettingProxiesCalledForSource = function(name) {

						var nextExpected = testSources[_.keys(called).length];

						try {
							expect(name).to.equal(nextExpected);
						} catch (error) {
							return done(error);
						}

						called[name] = true;
					};

					_.each(testSources, function(name) {
						ProxyLists.addSource(name, {
							getProxies: function(options) {
								var emitter = options.newEventEmitter();
								var onEnd = _.bind(emitter.emit, emitter, 'end');
								_.defer(_.bind(gettingProxiesCalledForSource, undefined, name));
								_.defer(onEnd);
								return emitter;
							}
						});
					});

					var options = {
						sourcesDir: null,
						series: true,
					};

					ProxyLists.getProxies(options).on('end', function() {

						try {
							expect(_.keys(called)).to.have.length(testSources.length);
						} catch (error) {
							return done(error);
						}

						done();
					});
				});
			});

			describe('FALSE', function() {

				it('should get proxies from all sources in parallel', function(done) {

					var testSources = ['somewhere', 'somewhere-else'];
					var gettingProxiesFromSource = {};

					_.each(testSources, function(name) {
						ProxyLists.addSource(name, {
							getProxies: function(options) {
								gettingProxiesFromSource[name] = true;
								return options.newEventEmitter();
							}
						});
					});

					var options = {
						sourcesDir: null,
						series: false,
					};

					ProxyLists.getProxies(options);

					_.defer(function() {
						_.each(testSources, function(name) {
							expect(gettingProxiesFromSource[name]).to.equal(true);
						});
						done();
					});
				});
			});
		});
	});

	it('calls dataSourcer.close method after end event', function(done) {

		var sourceNames = ['one', 'two', 'three'];
		_.each(sourceNames, function(name) {
			ProxyLists.addSource(name, {
				getProxies: function(options) {
					var emitter = options.newEventEmitter();
					_.defer(function() {
						emitter.emit('end');
					});
					return emitter;
				}
			});
		});

		var closeCalled = false;
		ProxyLists.DataSourcer.prototype.close = function() {
			closeCalled = true;
			return originalDataSourcerClose.apply(this, arguments);
		};

		ProxyLists.getProxies({
			sourcesDir: null,
		}).on('end', function() {
			try {
				expect(closeCalled).to.equal(true);
			} catch (error) {
				return done(error);
			}
			done();
		});
	});

	describe('health checks', function() {

		var sourcesWhiteList = (process.env.SOURCES && process.env.SOURCES.split(',')) || null;
		var dataSourcer = ProxyLists.prepareDataSourcer();
		var sources = dataSourcer.listSources({
			sourcesWhiteList: sourcesWhiteList,
		});

		_.each(sources, function(source) {

			var definition = dataSourcer.sources[source.name];

			describe(source.name, function() {

				it('"getProxies" function exists', function() {
					expect(definition.getProxies).to.be.a('function');
				});

				it('should return valid proxies', function(done) {

					if (process.env.TRAVIS_CI) {
						console.log('Skipping this test because it doesn\'t run well on travis-ci platform.');
						return this.skip();
					}

					this.timeout(60000);

					var options = {
						filterMode: 'loose',
						countries: null,
						anonymityLevels: null,
						protocols: null,
						sample: true,
						sampleDataLimit: 200,
						// browser: {
						// 	headless: false,
						// },
					};

					done = _.once(done);

					var proxies = [];
					ProxyLists.getProxiesFromSource(source.name, options)
						.on('data', function(_proxies) {
							proxies.push.apply(proxies, _proxies);
						})
						.on('error', done)
						.once('end', function() {
							try {
								expect(proxies).to.be.an('array');
								expect(proxies).to.not.have.length(0);
								var invalidProxies = _.reject(proxies, function(proxy) {
									return ProxyLists.isValidProxy(proxy);
								});
								var percentInvalid = (invalidProxies.length / proxies.length) * 100;
								// Allow up to 40% of the proxies to be invalid.
								if (percentInvalid > 40) {
									// Print up to 10 invalid proxies for debugging.
									console.log(invalidProxies.slice(0, Math.min(10, invalidProxies.length)));
									throw new Error('Too many invalid proxies from source: "' + source.name + '"');
								}
							} catch (error) {
								return done(error);
							}
							done();
						});
				});
			});
		});
	});
});
<commit_msg>can use slowMo option when debugging w/ browser<commit_after>'use strict';

var _ = require('underscore');
var expect = require('chai').expect;

var ProxyLists = require('../../index');

describe('getProxies([options, ]cb)', function() {

	var originalDataSourcerClose;
	beforeEach(function() {
		originalDataSourcerClose = ProxyLists.DataSourcer.prototype.close;
	});

	afterEach(function() {
		ProxyLists.DataSourcer.prototype.close = originalDataSourcerClose;
		ProxyLists._sources = [];
	});

	it('should be a function', function() {
		expect(ProxyLists.getProxies).to.be.a('function');
	});

	it('should call getProxies() for all sources', function(done) {

		try {
			var testSources = ['somewhere', 'somewhere-else'];
			var called = {};

			_.each(testSources, function(name) {
				ProxyLists.addSource(name, {
					getProxies: function(options) {
						var emitter = options.newEventEmitter();
						_.defer(function() {
							called[name] = true;
							emitter.emit('end');
						});
						return emitter;
					}
				});
			});

			ProxyLists.getProxies({
				sourcesDir: null,
			}).on('end', function() {

				try {
					_.each(testSources, function(name) {
						expect(called[name]).to.equal(true);
					});
				} catch (error) {
					return done(error);
				}

				done();
			});
		} catch (error) {
			return done(error);
		}
	});

	it('source with ipv6 addresses', function(done) {

		ProxyLists.addSource('ipv6test', {
			getProxies: function(options) {
				var emitter = options.newEventEmitter();
				var onData = _.bind(emitter.emit, emitter, 'data');
				var onEnd = _.bind(emitter.emit, emitter, 'end');
				_.defer(onData, [{
					ipAddress: '1200:0000:AB00:1234:0000:2552:7777:1313',
					port: 80,
					anonymityLevel: 'transparent'
				}])
				_.defer(onEnd);
				return emitter;
			}
		});

		ProxyLists.getProxies({
			sourcesDir: null,
		}).on('end', function() {
			done();
		});
	});

	describe('options', function() {

		describe('series', function() {

			describe('TRUE', function() {

				it('should get proxies from all sources in series', function(done) {

					var testSources = ['somewhere', 'somewhere-else'];
					var called = {};

					var gettingProxiesCalledForSource = function(name) {

						var nextExpected = testSources[_.keys(called).length];

						try {
							expect(name).to.equal(nextExpected);
						} catch (error) {
							return done(error);
						}

						called[name] = true;
					};

					_.each(testSources, function(name) {
						ProxyLists.addSource(name, {
							getProxies: function(options) {
								var emitter = options.newEventEmitter();
								var onEnd = _.bind(emitter.emit, emitter, 'end');
								_.defer(_.bind(gettingProxiesCalledForSource, undefined, name));
								_.defer(onEnd);
								return emitter;
							}
						});
					});

					var options = {
						sourcesDir: null,
						series: true,
					};

					ProxyLists.getProxies(options).on('end', function() {

						try {
							expect(_.keys(called)).to.have.length(testSources.length);
						} catch (error) {
							return done(error);
						}

						done();
					});
				});
			});

			describe('FALSE', function() {

				it('should get proxies from all sources in parallel', function(done) {

					var testSources = ['somewhere', 'somewhere-else'];
					var gettingProxiesFromSource = {};

					_.each(testSources, function(name) {
						ProxyLists.addSource(name, {
							getProxies: function(options) {
								gettingProxiesFromSource[name] = true;
								return options.newEventEmitter();
							}
						});
					});

					var options = {
						sourcesDir: null,
						series: false,
					};

					ProxyLists.getProxies(options);

					_.defer(function() {
						_.each(testSources, function(name) {
							expect(gettingProxiesFromSource[name]).to.equal(true);
						});
						done();
					});
				});
			});
		});
	});

	it('calls dataSourcer.close method after end event', function(done) {

		var sourceNames = ['one', 'two', 'three'];
		_.each(sourceNames, function(name) {
			ProxyLists.addSource(name, {
				getProxies: function(options) {
					var emitter = options.newEventEmitter();
					_.defer(function() {
						emitter.emit('end');
					});
					return emitter;
				}
			});
		});

		var closeCalled = false;
		ProxyLists.DataSourcer.prototype.close = function() {
			closeCalled = true;
			return originalDataSourcerClose.apply(this, arguments);
		};

		ProxyLists.getProxies({
			sourcesDir: null,
		}).on('end', function() {
			try {
				expect(closeCalled).to.equal(true);
			} catch (error) {
				return done(error);
			}
			done();
		});
	});

	describe('health checks', function() {

		var sourcesWhiteList = (process.env.SOURCES && process.env.SOURCES.split(',')) || null;
		var dataSourcer = ProxyLists.prepareDataSourcer();
		var sources = dataSourcer.listSources({
			sourcesWhiteList: sourcesWhiteList,
		});

		_.each(sources, function(source) {

			var definition = dataSourcer.sources[source.name];

			describe(source.name, function() {

				it('"getProxies" function exists', function() {
					expect(definition.getProxies).to.be.a('function');
				});

				it('should return valid proxies', function(done) {

					if (process.env.TRAVIS_CI) {
						console.log('Skipping this test because it doesn\'t run well on travis-ci platform.');
						return this.skip();
					}

					this.timeout(60000);

					var options = {
						filterMode: 'loose',
						countries: null,
						anonymityLevels: null,
						protocols: null,
						sample: true,
						sampleDataLimit: 200,
						// browser: {
						// 	headless: false,
						// 	slowMo: 50,
						// },
					};

					done = _.once(done);

					var proxies = [];
					ProxyLists.getProxiesFromSource(source.name, options)
						.on('data', function(_proxies) {
							proxies.push.apply(proxies, _proxies);
						})
						.on('error', done)
						.once('end', function() {
							try {
								expect(proxies).to.be.an('array');
								expect(proxies).to.not.have.length(0);
								var invalidProxies = _.reject(proxies, function(proxy) {
									return ProxyLists.isValidProxy(proxy);
								});
								var percentInvalid = (invalidProxies.length / proxies.length) * 100;
								// Allow up to 40% of the proxies to be invalid.
								if (percentInvalid > 40) {
									// Print up to 10 invalid proxies for debugging.
									console.log(invalidProxies.slice(0, Math.min(10, invalidProxies.length)));
									throw new Error('Too many invalid proxies from source: "' + source.name + '"');
								}
							} catch (error) {
								return done(error);
							}
							done();
						});
				});
			});
		});
	});
});
<|endoftext|>