<commit_before>'use strict';

function download(tabId, downloadRequest){
    new Download(downloadRequest, tabId).process();
}

function Download(downloadRequest, tabId){
    this.downloadRequest    = downloadRequest;
    this.tabId              = tabId;
    this.path               = '';
    this.filename           = '';
    this.basename           = '';
    this.duplicateCheckTry  = 0;
}

Download.prototype = {
    process: async function(){
        let extractedFilename;

        if (!this.downloadRequest.template.filename) {
            this.downloadRequest.template.filename = '::both_filenames::'; // default behavior
        }

        this.path = filenameTools.replacePathPlaceholders(this.downloadRequest);

        if (this.filenameExtractionRequired()) {
            Object.assign(this, filenameTools.extractFilename(this.downloadRequest.src));
            if (!this.filename) {
                this.filename = await this.getFilenameFromHeaders();
            }
            extractedFilename = this.filename;
        }

        this.filename = filenameTools.replaceFilenamePlaceholders(this.downloadRequest, extractedFilename);
        this.download();
    },

    getFilenameFromHeaders: async function(){
        const headers = await this.getHeaders();
        let tmpFilename,
            extension;

        if (!headers) {
            return this.getFallbackFilename();
        }

        tmpFilename = headers.contentDisposition && headers.contentDisposition.match(/^.+filename\*?=(.{0,20}')?([^;]*);?$/i);
        extension = headers.contentType ? ('.' + headers.contentType.match(/\w+\/(\w+)/)[1].replace('jpeg', 'jpg')) : '';

        return tmpFilename ?
            decodeURI(tmpFilename[2]).replace(/"/g, '') :
            (this.basename || filenameTools.getTimestamp()) + extension;
    },

    getHeaders: async function(){
        let response = await fetch(this.downloadRequest.src, {method: 'HEAD'});

        if ([404, 405, 501].includes(response.status)) { // HEAD request method is not supported / allowed / implemented by server
            response = await fetch(this.downloadRequest.src, {method: 'GET'});
        }

        return response.ok ? {
            contentType: response.headers.get('content-type'),
            contentDisposition: response.headers.get('content-disposition'),
        } : null;
    },

    getFallbackFilename: function(){
        const filenameTry = (this.downloadRequest.pageInfo.title || '').match(/[^\s]+\.(jpg|jpeg|png|gif|bmp|webm|mp4|ogg|mp3)/i);
        return filenameTry ? filenameTry[0] : filenameTools.getTimestamp();
    },

    download: function(){
        const finalPath = filenameTools.preparePath(this.path),
            finalFilename = filenameTools.prepareFilename(this.filename);

        chrome.downloads.download({
                url             : this.downloadRequest.src,
                filename        : finalPath + finalFilename,
                saveAs          : this.downloadRequest.showSaveDialog,
                conflictAction  : 'uniquify',
            },
            downloadId => {
                if (chrome.extension.lastError) {return;}
                this.checkForDuplicate(finalFilename, downloadId);
            }
        );
    },

    /*
    * If resulted filename is not the same as the filename passed to downloads.download function,
    * then it was modified by 'uniquify' conflict action of WebExt API,
    * and user should be warned that he saved already existing file.
    */
    checkForDuplicate: function(originalFilename, downloadId){
        chrome.downloads.search({
            id: downloadId
        }, downloadItems => {
            if (!downloadItems[0]) {
            	this.checkForDuplicateRetry(originalFilename, downloadId);
            	return;
            }
            if (downloadItems[0].filename && !downloadItems[0].filename.endsWith(originalFilename)) {
                chrome.tabs.sendMessage(this.tabId, 'duplicate_warning');
            }
        });
    },

    /*
    * Hack for once broken(?) downloads.search, now it can't find download if called instantly from downloads.download callback
    */
    checkForDuplicateRetry: function(originalFilename, downloadId){
        if (this.duplicateCheckTry > 5) {return;}
        this.duplicateCheckTry++;
        setTimeout(() => this.checkForDuplicate(originalFilename, downloadId), 50);
    },

    filenameExtractionRequired: function(){
        const filenameTemplate = this.downloadRequest.template.filename;
        return (
            filenameTemplate.includes('::filename::') ||
            (filenameTemplate.includes('::both_filenames::') && !this.downloadRequest.useOriginalName)
        );
    },
};

const filenameTools = {
    replacePathPlaceholders: function(dlRequest){
        // TODO: don't use *filename* replacements
        return this.replacePlaceholders(dlRequest.template.path, dlRequest)
    },

    replaceFilenamePlaceholders: function(dlRequest, extractedFilename){
        return this.replacePlaceholders(dlRequest.template.filename, dlRequest, extractedFilename)
    },

    replacePlaceholders: function(template, dlRequest, extractedFilename = null){
        let string = template;
        const placeholders = {
            '::domain::'            : () => this.trimForbiddenWinChars(dlRequest.pageInfo.domain),
            '::title::'             : () => this.trimForbiddenWinChars(dlRequest.pageInfo.title),
            '::thread_number::'     : () => this.trimForbiddenWinChars(dlRequest.pageInfo.threadNum),
            '::board_name::'        : () => this.trimForbiddenWinChars(dlRequest.pageInfo.boardName),
            '::date::'              : this.getDatetimeString,
            '::time::'              : this.getTimestamp,
            '::filename::'          : () => extractedFilename,
            '::original_filename::' : () => dlRequest.originalName,
            '::both_filenames::'    : () => dlRequest.useOriginalName ? dlRequest.originalName : extractedFilename,
        };

        string.includes(':') && Object.entries(placeholders).forEach(([placeholder, replacement]) => {
            string = string.replace(placeholder, replacement());
        });

        return string;
    },

    /*
    * Decodes url, cuts possible GET parameters, extracts filename from the url.
    * Usually filename is located at the end, after last "/" symbol, but sometimes
    * it might be somewhere in the middle between "/" symbols.
    * That's why it's important to extract filename manually by looking for the last sub-string
    * with pre-known extension located between "/" symbols.
    * WebExt API is incapable of extracting such filenames.
    * Cheers pineapple.
    */
    extractFilename: function(originalUrl){
        const url = decodeURI(originalUrl).replace(/^.*https?:\/\/([^/]+)\/+/, '').split(/[?#]/)[0].replace(/:\w+$/, '').replace(/\/{2,}/, '/'),
            filenameTry = url.match(/^([^/]+\/)*([^/]+\.(jpg|jpeg|png|gif|bmp|webm|mp4|ogg|mp3))([\/][^.]+)?$/i);

        return filenameTry ?
            {filename: filenameTry[2]} :
            {basename: url.replace(/\/480$/, '').split('/').pop()}; // '/480' removal is a hack for tumblr videos
    },

    trimForbiddenWinChars: function(string){
        return (string || '').replace(/[/\\:*?"<>|\x09\u0080-\u008f]/g, '').replace(/^\.+|\.+$/g, '');
    },

    /*
    * If the path is not empty then trims leading/trailing slashes/backslashes
    * and adds a single slash to the end for concating with filename.
    * Doesn't matter which slashes are used in save path, WebExt API recognizes both.
    */
    preparePath: function(path){
        return path && (path.replace(/^\\+|\\+$|^\/+|\/+$/, '') + '/');
    },

    prepareFilename: function(filename){
        let decodedFilename;
        try {
            decodedFilename = decodeURI(filename);
        } catch (e) { // malformed URI sequence
            decodedFilename = filename;
        }

        return this.trimForbiddenWinChars(decodedFilename);
    },

    getDatetimeString: function(){
        return new Date().toISOString().replace('T', '_').replace(/\..+/g, '').replace(/[^\d_]/g, '');
    },

    getTimestamp: function(){
        return Date.now();
    },
};
<commit_msg>more straightforward filename extraction<commit_after>'use strict';

function download(tabId, downloadRequest){
    new Download(downloadRequest, tabId).process();
}

function Download(downloadRequest, tabId){
    this.downloadRequest    = downloadRequest;
    this.tabId              = tabId;
    this.path               = '';
    this.duplicateCheckTry  = 0;
}

Download.prototype = {
    process: async function(){
        let extractedFilename;

        if (!this.downloadRequest.template.filename) {
            this.downloadRequest.template.filename = '::both_filenames::'; // default behavior
        }

        if (this.filenameExtractionRequired()) {
            const extractedFromSrc = filenameTools.extractFilename(this.downloadRequest.src);
            if (extractedFromSrc.filename) {
                extractedFilename = extractedFromSrc.filename;
            } else {
                const extractedFromHeaders = await this.getFilenameFromHeaders();
                if (extractedFromHeaders.filename) {
                    extractedFilename = extractedFromHeaders.filename;
                } else {
                    extractedFilename = this.getFallbackFilename(extractedFromHeaders.extension)
                }
            }
        }

        this.download(
            filenameTools.replacePathPlaceholders(this.downloadRequest),
            filenameTools.replaceFilenamePlaceholders(this.downloadRequest, extractedFilename)
        );
    },

    getFilenameFromHeaders: async function(){
        const headers = await this.getHeaders();
        let tmpFilename;

        if (!headers) {
            return null;
        }

        tmpFilename = headers.contentDisposition && headers.contentDisposition.match(/^.+filename\*?=(.{0,20}')?([^;]*);?$/i);

        return tmpFilename ?
            {filename: decodeURI(tmpFilename[2]).replace(/"/g, '')} :
            {extension: headers.contentType && headers.contentType.match(/\w+\/(\w+)/)[1].replace('jpeg', 'jpg')};
    },

    getHeaders: async function(){
        let response = await fetch(this.downloadRequest.src, {method: 'HEAD'});

        if ([404, 405, 501].includes(response.status)) { // HEAD request method is not supported / allowed / implemented by server
            response = await fetch(this.downloadRequest.src, {method: 'GET'});
        }

        return response.ok ? {
            contentType: response.headers.get('content-type'),
            contentDisposition: response.headers.get('content-disposition'),
        } : null;
    },

    getFallbackFilename: function(possibleExtension){
        const filenameTry = (this.downloadRequest.pageInfo.title || '').match(/[^\s]+\.(jpg|jpeg|png|gif|bmp|webm|mp4|ogg|mp3)/i);
        return filenameTry ? filenameTry[0] : `${filenameTools.getTimestamp()}.${possibleExtension || 'jpg'}`;
    },

    download: function(path, filename){
        const finalPath = filenameTools.preparePath(path),
            finalFilename = filenameTools.prepareFilename(filename);

        chrome.downloads.download({
                url             : this.downloadRequest.src,
                filename        : finalPath + finalFilename,
                saveAs          : this.downloadRequest.showSaveDialog,
                conflictAction  : 'uniquify',
            },
            downloadId => {
                if (chrome.extension.lastError) {return;}
                this.checkForDuplicate(finalFilename, downloadId);
            }
        );
    },

    /*
    * If resulted filename is not the same as the filename passed to downloads.download function,
    * then it was modified by 'uniquify' conflict action of WebExt API,
    * and user should be warned that he saved already existing file.
    */
    checkForDuplicate: function(originalFilename, downloadId){
        chrome.downloads.search({
            id: downloadId
        }, downloadItems => {
            if (!downloadItems[0]) {
            	this.checkForDuplicateRetry(originalFilename, downloadId);
            	return;
            }
            if (downloadItems[0].filename && !downloadItems[0].filename.endsWith(originalFilename)) {
                chrome.tabs.sendMessage(this.tabId, 'duplicate_warning');
            }
        });
    },

    /*
    * Hack for once broken(?) downloads.search, now it can't find download if called instantly from downloads.download callback
    */
    checkForDuplicateRetry: function(originalFilename, downloadId){
        if (this.duplicateCheckTry > 5) {return;}
        this.duplicateCheckTry++;
        setTimeout(() => this.checkForDuplicate(originalFilename, downloadId), 50);
    },

    filenameExtractionRequired: function(){
        const filenameTemplate = this.downloadRequest.template.filename;
        return (
            filenameTemplate.includes('::filename::') ||
            (filenameTemplate.includes('::both_filenames::') && !this.downloadRequest.useOriginalName)
        );
    },
};

const filenameTools = {
    replacePathPlaceholders: function(dlRequest){
        // TODO: don't use *filename* replacements
        return this.replacePlaceholders(dlRequest.template.path, dlRequest)
    },

    replaceFilenamePlaceholders: function(dlRequest, extractedFilename){
        return this.replacePlaceholders(dlRequest.template.filename, dlRequest, extractedFilename)
    },

    replacePlaceholders: function(template, dlRequest, extractedFilename = null){
        let string = template;
        const placeholders = {
            '::domain::'            : () => this.trimForbiddenWinChars(dlRequest.pageInfo.domain),
            '::title::'             : () => this.trimForbiddenWinChars(dlRequest.pageInfo.title),
            '::thread_number::'     : () => this.trimForbiddenWinChars(dlRequest.pageInfo.threadNum),
            '::board_name::'        : () => this.trimForbiddenWinChars(dlRequest.pageInfo.boardName),
            '::date::'              : this.getDatetimeString,
            '::time::'              : this.getTimestamp,
            '::filename::'          : () => extractedFilename,
            '::original_filename::' : () => dlRequest.originalName,
            '::both_filenames::'    : () => dlRequest.useOriginalName ? dlRequest.originalName : extractedFilename,
        };

        string.includes(':') && Object.entries(placeholders).forEach(([placeholder, replacement]) => {
            string = string.replace(placeholder, replacement());
        });

        return string;
    },

    /*
    * Decodes url, cuts possible GET parameters, extracts filename from the url.
    * Usually filename is located at the end, after last "/" symbol, but sometimes
    * it might be somewhere in the middle between "/" symbols.
    * That's why it's important to extract filename manually by looking for the last sub-string
    * with pre-known extension located between "/" symbols.
    * WebExt API is incapable of extracting such filenames.
    * Cheers pineapple.
    */
    extractFilename: function(originalUrl){
        const url = decodeURI(originalUrl).replace(/^.*https?:\/\/([^/]+)\/+/, '').split(/[?#]/)[0].replace(/:\w+$/, '').replace(/\/{2,}/, '/'),
            filenameTry = url.match(/^([^/]+\/)*([^/]+\.(jpg|jpeg|png|gif|bmp|webm|mp4|ogg|mp3))([\/][^.]+)?$/i);

        return filenameTry ?
            {filename: filenameTry[2]} :
            {basename: url.replace(/\/480$/, '').split('/').pop()}; // '/480' removal is a hack for tumblr videos
    },

    trimForbiddenWinChars: function(string){
        return (string || '').replace(/[/\\:*?"<>|\x09\u0080-\u008f]/g, '').replace(/^\.+|\.+$/g, '');
    },

    /*
    * If the path is not empty then trims leading/trailing slashes/backslashes
    * and adds a single slash to the end for concating with filename.
    * Doesn't matter which slashes are used in save path, WebExt API recognizes both.
    */
    preparePath: function(path){
        return path && (path.replace(/^\\+|\\+$|^\/+|\/+$/, '') + '/');
    },

    prepareFilename: function(filename){
        let decodedFilename;
        try {
            decodedFilename = decodeURI(filename);
        } catch (e) { // malformed URI sequence
            decodedFilename = filename;
        }

        return this.trimForbiddenWinChars(decodedFilename);
    },

    getDatetimeString: function(){
        return new Date().toISOString().replace('T', '_').replace(/\..+/g, '').replace(/[^\d_]/g, '');
    },

    getTimestamp: function(){
        return Date.now();
    },
};
<|endoftext|>


<commit_before>'use strict';

function check(){
    var integer = core_random_integer({
      'max': 100000,
      'todo': 'ceil',
    });
    var result = ' was generated. You ';

    result += parseInt(document.getElementById('answer').value, 10) === integer
      ? 'might have ESP!'
      : 'probably don\'t have ESP.';

    document.getElementById('integer').innerHTML = integer;
    document.getElementById('result').innerHTML = result;
}

function repo_init(){
    core_input_binds_add({
      'keybinds': {
        13: {
          'todo': check,
        },
      },
    });

    document.getElementById('answer').focus();
    document.getElementById('check').onclick = check;
}
<commit_msg>switched core input code to more general event code<commit_after>'use strict';

function check(){
    var integer = core_random_integer({
      'max': 100000,
      'todo': 'ceil',
    });
    var result = ' was generated. You ';

    result += parseInt(document.getElementById('answer').value, 10) === integer
      ? 'might have ESP!'
      : 'probably don\'t have ESP.';

    document.getElementById('integer').innerHTML = integer;
    document.getElementById('result').innerHTML = result;
}

function repo_init(){
    core_events_bind({
      'keybinds': {
        13: {
          'todo': check,
        },
      },
    });

    document.getElementById('answer').focus();
    document.getElementById('check').onclick = check;
}
<|endoftext|>


<commit_before>'use strict';
Cryptodog.xmpp = {};
Cryptodog.xmpp.currentStatus = 'online';
Cryptodog.xmpp.connection = null;

Cryptodog.xmpp.defaultServer = {
	name: 'Cryptodog',
	domain: 'crypto.dog',
	conference: 'conference.crypto.dog',
	relay: 'https://crypto.dog/http-bind'
};

Cryptodog.xmpp.currentServer = {};

$(window).ready(function() {
Cryptodog.storage.removeItem('serverName');
Cryptodog.storage.removeItem('domain');
Cryptodog.storage.removeItem('conferenceServer');
Cryptodog.storage.removeItem('relay');

// Load custom server settings
Cryptodog.storage.getItem('serverName', function(key){
	Cryptodog.xmpp.currentServer.name = key ? key : Cryptodog.xmpp.defaultServer.name;
});
Cryptodog.storage.getItem('domain', function(key){
	Cryptodog.xmpp.currentServer.domain = key ? key : Cryptodog.xmpp.defaultServer.domain;
});
Cryptodog.storage.getItem('conferenceServer', function(key){
	Cryptodog.xmpp.currentServer.conference = key ? key : Cryptodog.xmpp.defaultServer.conference;
});
Cryptodog.storage.getItem('relay', function(key){
	Cryptodog.xmpp.currentServer.relay = key ? key : Cryptodog.xmpp.defaultServer.relay;
});

// Prepares necessary encryption key operations before XMPP connection.
// Shows a progress bar while doing so.
Cryptodog.xmpp.showKeyPreparationDialog = function(callback) {
	// Key storage currently disabled as we are not yet sure if this is safe to do.
	// Cryptodog.storage.setItem('multiPartyKey', Cryptodog.multiParty.genPrivateKey())
	//else {
	Cryptodog.me.mpPrivateKey = Cryptodog.multiParty.genPrivateKey()
	//}
	Cryptodog.me.mpPublicKey = Cryptodog.multiParty.genPublicKey(
		Cryptodog.me.mpPrivateKey
	)
	Cryptodog.me.mpFingerprint = Cryptodog.multiParty.genFingerprint()
	// If we already have keys, just skip to the callback.
	if (Cryptodog.me.otrKey) {
		callback()
		return
	}
	$('#loginInfo').text(Cryptodog.locale['loginMessage']['generatingKeys'])

	// Add delay to key generation when on the file protocol
	// Since the UI freezes when generating keys without WebWorkers
	if (window.location.protocol === 'file:'){
		setTimeout(function(){Cryptodog.xmpp.prepareKeys(callback)}, 100)
	}
	else {
		Cryptodog.xmpp.prepareKeys(callback)
	}
}

// See above.
Cryptodog.xmpp.prepareKeys = function(callback) {
	// Create DSA key for OTR.
	// file protocol doesn't support WebWorkers
	if (window.location.protocol === 'file:'){
		Cryptodog.me.otrKey = new DSA()
		if (callback) { callback() }
	}
	else {
		DSA.createInWebWorker({
			path: 'js/workers/dsa.js',
			seed: Cryptodog.random.generateSeed
		}, function(key) {
			Cryptodog.me.otrKey = key
			// Key storage currently disabled as we are not yet sure if this is safe to do.
			//	Cryptodog.storage.setItem('myKey', JSON.stringify(Cryptodog.me.otrKey))
			if (callback) { callback() }
		})
	}

}

// Connect anonymously and join conversation.
Cryptodog.xmpp.connect = function() {
	Cryptodog.me.conversation = Strophe.xmlescape($('#conversationName').val())
	Cryptodog.me.nickname = Strophe.xmlescape($('#nickname').val())
	Cryptodog.xmpp.connection = new Strophe.Connection(Cryptodog.xmpp.currentServer.relay)
	Cryptodog.xmpp.connection.connect(Cryptodog.xmpp.currentServer.domain, null, function(status) {
		if (status === Strophe.Status.CONNECTING) {
			$('#loginInfo').text(Cryptodog.locale['loginMessage']['connecting'])
		}
		else if (status === Strophe.Status.CONNECTED) {
			Cryptodog.xmpp.connection.muc.join(
				Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
				Cryptodog.me.nickname,
				function(message) {
					if (Cryptodog.xmpp.onMessage(message)) { return true }
				},
				function(presence) {
					if (Cryptodog.xmpp.onPresence(presence)) { return true }
				}
			)
			Cryptodog.xmpp.onConnected()
			document.title = Cryptodog.me.nickname + '@' + Cryptodog.me.conversation
			$('.conversationName').text(document.title)
			Cryptodog.storage.setItem('nickname', Cryptodog.me.nickname)
		}
		else if ((status === Strophe.Status.CONNFAIL) || (status === Strophe.Status.DISCONNECTED)) {
			if (Cryptodog.loginError) {
				Cryptodog.xmpp.reconnect()
			}
		}
	})
}

// Executes on successfully completed XMPP connection.
Cryptodog.xmpp.onConnected = function() {
	afterConnect()
	$('#loginInfo').text('✓')
	$('#status').attr('src', 'img/icons/checkmark.svg');
	$('#buddy-groupChat,#status').show()
	$('#buddy-groupChat').insertBefore('#buddiesOnline')
	$('#fill').stop().animate({
		'width': '100%', 'opacity': '1'
	}, 250, 'linear')
	window.setTimeout(function() {
		$('#dialogBoxClose').click()
	}, 400)
	window.setTimeout(function() {
		$('#loginOptions,#languages,#customServerDialog').fadeOut(200)
		$('#version,#logoText,#loginInfo,#info').fadeOut(200)
		$('#header').animate({'background-color': '#444'})
		$('.logo').animate({'margin': '-11px 5px 0 0'})
		$('#login').fadeOut(200, function() {
			$('#conversationInfo').fadeIn()
			$('#buddy-groupChat').click(function() {
				Cryptodog.onBuddyClick($(this))
			})
			$('#buddy-groupChat').click()
			$('#conversationWrapper').fadeIn()
			$('#optionButtons').fadeIn()
			$('#footer').delay(200).animate({'height': 60}, function() {
				$('#userInput').fadeIn(200, function() {
					$('#userInputText').focus()
				})
			})
			$('#buddyWrapper').slideDown()
		})
	}, 800)
	Cryptodog.loginError = true
}

// Reconnect to the same chatroom, on accidental connection loss.
Cryptodog.xmpp.reconnect = function() {
	if (Cryptodog.xmpp.connection) {
	    Cryptodog.xmpp.connection.reset()
	}
	Cryptodog.xmpp.connection = new Strophe.Connection(Cryptodog.xmpp.currentServer.relay)
	Cryptodog.xmpp.connection.connect(Cryptodog.xmpp.currentServer.domain, null, function(status) {
		if (status === Strophe.Status.CONNECTING) {
			$('.conversationName').animate({'background-color': '#F00'})
		}
		else if (status === Strophe.Status.CONNECTED) {
			afterConnect()
			Cryptodog.xmpp.connection.muc.join(
				Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
				Cryptodog.me.nickname
			)
		}
		else if ((status === Strophe.Status.CONNFAIL) || (status === Strophe.Status.DISCONNECTED)) {
			if (Cryptodog.loginError) {
				window.setTimeout(function() {
					Cryptodog.xmpp.reconnect()
				}, 5000)
			}
		}
	})
}

// Handle incoming messages from the XMPP server.
Cryptodog.xmpp.onMessage = function(message) {
	var nickname = cleanNickname($(message).attr('from'))
	var body = $(message).find('body').text()
	var type = $(message).attr('type')
	// If archived message, ignore.
	if ($(message).find('delay').length !== 0) {
		return true
	}
	//If message is from me, ignore.
	if (nickname === Cryptodog.me.nickname) {
		return true
	}
	// If message is from someone not on buddy list, ignore.
	if (!Cryptodog.buddies.hasOwnProperty(nickname)) {
		return true
	}
	// Check if message has a 'composing' notification.
	if ($(message).find('composing').length && !body.length) {
		$('#buddy-' + Cryptodog.buddies[nickname].id).addClass('composing')
		return true
	}
	// Check if message has a 'paused' (stopped writing) notification.
	if ($(message).find('paused').length) {
		$('#buddy-' + Cryptodog.buddies[nickname].id).removeClass('composing')
	}
	// Check if message is a group chat message.
	else if (type === 'groupchat' && body.length) {
		$('#buddy-' + Cryptodog.buddies[nickname].id).removeClass('composing')
		body = Cryptodog.multiParty.receiveMessage(nickname, Cryptodog.me.nickname, body)
		if (typeof(body) === 'string') {
			Cryptodog.addToConversation(body, nickname, 'groupChat', 'message')
		}
	}
	// Check if this is a private OTR message.
	else if (type === 'chat') {
		$('#buddy-' + Cryptodog.buddies[nickname].id).removeClass('composing')
		Cryptodog.buddies[nickname].otr.receiveMsg(body)
	}
	return true
}

// Handle incoming presence updates from the XMPP server.
Cryptodog.xmpp.onPresence = function(presence) {
	var status
	var nickname = cleanNickname($(presence).attr('from'))
	// If invalid nickname, do not process
	if ($(presence).attr('type') === 'error') {
		if ($(presence).find('error').attr('code') === '409') {
			// Delay logout in order to avoid race condition with window animation
			window.setTimeout(function() {
				Cryptodog.logout()
				Cryptodog.UI.loginFail(Cryptodog.locale['loginMessage']['nicknameInUse'])
			}, 3000)
			return false
		}
		return true
	}
	// Ignore if presence status is coming from myself
	if (nickname === Cryptodog.me.nickname) {
		return true
	}
	// Detect nickname change (which may be done by non-Cryptodog XMPP clients)
	if ($(presence).find('status').attr('code') === '303') {
		Cryptodog.removeBuddy(nickname)
		return true
	}
	// Detect buddy going offline.
	if ($(presence).attr('type') === 'unavailable') {
		Cryptodog.removeBuddy(nickname)
		return true
	}
	// Create buddy element if buddy is new.
	else if (!Cryptodog.buddies.hasOwnProperty(nickname)) {
		Cryptodog.addBuddy(nickname, null, 'online')
		for (var u = 0; u < 4000; u += 2000) {
			window.setTimeout(Cryptodog.xmpp.sendPublicKey, u, nickname)
		}
		Cryptodog.xmpp.sendStatus(); // Propagate away status to newcomers.
	}
	// Handle buddy status change to 'available'.
	else if (
		$(presence).find('show').text() === '' ||
		$(presence).find('show').text() === 'chat'
	) {
		status = 'online'
	}
	// Handle buddy status change to 'away'.
	else {
		status = 'away'
	}
	// Perform status change.
	Cryptodog.buddyStatus(nickname, status)
	return true
}

// Send your own multiparty public key to `nickname`, via XMPP-MUC.
Cryptodog.xmpp.sendPublicKey = function(nickname) {
	Cryptodog.xmpp.connection.muc.message(
		Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
		null, Cryptodog.multiParty.sendPublicKey(nickname), null, 'groupchat', 'active'
	)
}

// Send your current status to the XMPP server.
Cryptodog.xmpp.sendStatus = function() {
    var status = ''
	if (Cryptodog.xmpp.currentStatus === 'away') {
    	status = 'away'
	}
	Cryptodog.xmpp.connection.muc.setStatus(
		Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
		Cryptodog.me.nickname, status, status
	)
}

// Executed (manually) after connection.
var afterConnect = function() {
	$('.conversationName').animate({'background-color': '#bb7a20'})
	Cryptodog.xmpp.connection.ibb.addIBBHandler(Cryptodog.otr.ibbHandler)
	/* jshint -W106 */
	Cryptodog.xmpp.connection.si_filetransfer.addFileHandler(Cryptodog.otr.fileHandler)
	/* jshint +W106 */

	// Send status upon (re)connect.
	Cryptodog.xmpp.sendStatus();
}

// Clean nickname so that it's safe to use.
var cleanNickname = function(nickname) {
	var clean = nickname.match(/\/([\s\S]+)/)
	if (clean) {
		return clean[1]
	}
	return false
}

})
<commit_msg>Minor refactoring, formatting of xmpp.js<commit_after>'use strict';
Cryptodog.xmpp = {};
Cryptodog.xmpp.currentStatus = 'online';
Cryptodog.xmpp.connection = null;

Cryptodog.xmpp.defaultServer = {
    name: 'Cryptodog',
    domain: 'crypto.dog',
    conference: 'conference.crypto.dog',
    relay: 'https://crypto.dog/http-bind'
};

Cryptodog.xmpp.currentServer = {};

$(window).ready(function() {
    // Load custom server settings
    Cryptodog.storage.getItem('serverName', function(key) {
        Cryptodog.xmpp.currentServer.name = key ? key : Cryptodog.xmpp.defaultServer.name;
    });
    Cryptodog.storage.getItem('domain', function(key) {
        Cryptodog.xmpp.currentServer.domain = key ? key : Cryptodog.xmpp.defaultServer.domain;
    });
    Cryptodog.storage.getItem('conferenceServer', function(key) {
        Cryptodog.xmpp.currentServer.conference = key ? key : Cryptodog.xmpp.defaultServer.conference;
    });
    Cryptodog.storage.getItem('relay', function(key) {
        Cryptodog.xmpp.currentServer.relay = key ? key : Cryptodog.xmpp.defaultServer.relay;
    });

    // Prepares necessary encryption key operations before XMPP connection.
    // Shows a progress bar while doing so.
    Cryptodog.xmpp.showKeyPreparationDialog = function(callback) {
        Cryptodog.me.mpPrivateKey = Cryptodog.multiParty.genPrivateKey();

        Cryptodog.me.mpPublicKey = Cryptodog.multiParty.genPublicKey(Cryptodog.me.mpPrivateKey);
        Cryptodog.me.mpFingerprint = Cryptodog.multiParty.genFingerprint();

        // If we already have keys, just skip to the callback.
        if (Cryptodog.me.otrKey) {
            callback();
            return;
        }

        $('#loginInfo').text(Cryptodog.locale['loginMessage']['generatingKeys']);

        // Add delay to key generation when on the file protocol
        // Since the UI freezes when generating keys without WebWorkers
        if (window.location.protocol === 'file:') {
            setTimeout(function() {
                Cryptodog.xmpp.prepareKeys(callback);
            }, 100);
        } else {
            Cryptodog.xmpp.prepareKeys(callback);
        }
    };

    // See above.
    Cryptodog.xmpp.prepareKeys = function(callback) {
        // Create DSA key for OTR.
        // file protocol doesn't support WebWorkers
        if (window.location.protocol === 'file:') {
            Cryptodog.me.otrKey = new DSA();

            if (callback) {
                callback();
            }
        } else {
            DSA.createInWebWorker(
                {
                    path: 'js/workers/dsa.js',
                    seed: Cryptodog.random.generateSeed
                },
                function(key) {
                    Cryptodog.me.otrKey = key;

                    if (callback) {
                        callback();
                    }
                }
            );
        }
    };

    // Connect anonymously and join conversation.
    Cryptodog.xmpp.connect = function() {
        Cryptodog.me.conversation = Strophe.xmlescape($('#conversationName').val());
        Cryptodog.me.nickname = Strophe.xmlescape($('#nickname').val());

        Cryptodog.xmpp.connection = new Strophe.Connection(Cryptodog.xmpp.currentServer.relay);

        Cryptodog.xmpp.connection.connect(Cryptodog.xmpp.currentServer.domain, null, function(status) {
            if (status === Strophe.Status.CONNECTING) {
                $('#loginInfo').text(Cryptodog.locale['loginMessage']['connecting']);
            } else if (status === Strophe.Status.CONNECTED) {
                Cryptodog.xmpp.connection.muc.join(
                    Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
                    Cryptodog.me.nickname,
                    function(message) {
                        if (Cryptodog.xmpp.onMessage(message)) {
                            return true;
                        }
                    },
                    function(presence) {
                        if (Cryptodog.xmpp.onPresence(presence)) {
                            return true;
                        }
                    }
                );
                Cryptodog.xmpp.onConnected();

                document.title = Cryptodog.me.nickname + '@' + Cryptodog.me.conversation;
                $('.conversationName').text(document.title);

                Cryptodog.storage.setItem('nickname', Cryptodog.me.nickname);
            } else if (status === Strophe.Status.CONNFAIL || status === Strophe.Status.DISCONNECTED) {
                if (Cryptodog.loginError) {
                    Cryptodog.xmpp.reconnect();
                }
            }
        });
    };

    // Executes on successfully completed XMPP connection.
    Cryptodog.xmpp.onConnected = function() {
        afterConnect();

        $('#loginInfo').text('✓');
        $('#status').attr('src', 'img/icons/checkmark.svg');
        $('#buddy-groupChat,#status').show();
        $('#buddy-groupChat').insertBefore('#buddiesOnline');
        $('#fill')
            .stop()
            .animate(
                {
                    width: '100%',
                    opacity: '1'
                },
                250,
                'linear'
            );

        window.setTimeout(function() {
            $('#dialogBoxClose').click();
        }, 400);

        window.setTimeout(function() {
            $('#loginOptions,#languages,#customServerDialog').fadeOut(200);
            $('#version,#logoText,#loginInfo,#info').fadeOut(200);
            $('#header').animate({ 'background-color': '#444' });
            $('.logo').animate({ margin: '-11px 5px 0 0' });

            $('#login').fadeOut(200, function() {
                $('#conversationInfo').fadeIn();

                $('#buddy-groupChat').click(function() {
                    Cryptodog.onBuddyClick($(this));
                });

                $('#buddy-groupChat').click();
                $('#conversationWrapper').fadeIn();
                $('#optionButtons').fadeIn();

                $('#footer')
                    .delay(200)
                    .animate({ height: 60 }, function() {
                        $('#userInput').fadeIn(200, function() {
                            $('#userInputText').focus();
                        });
                    });

                $('#buddyWrapper').slideDown();
            });
        }, 800);

        Cryptodog.loginError = true;
    };

    // Reconnect to the same chatroom, on accidental connection loss.
    Cryptodog.xmpp.reconnect = function() {
        if (Cryptodog.xmpp.connection) {
            Cryptodog.xmpp.connection.reset();
        }

        Cryptodog.xmpp.connection = new Strophe.Connection(Cryptodog.xmpp.currentServer.relay);

        Cryptodog.xmpp.connection.connect(Cryptodog.xmpp.currentServer.domain, null, function(status) {
            if (status === Strophe.Status.CONNECTING) {
                $('.conversationName').animate({ 'background-color': '#F00' });
            } else if (status === Strophe.Status.CONNECTED) {
                afterConnect();

                Cryptodog.xmpp.connection.muc.join(
                    Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
                    Cryptodog.me.nickname
                );
            } else if (status === Strophe.Status.CONNFAIL || status === Strophe.Status.DISCONNECTED) {
                if (Cryptodog.loginError) {
                    window.setTimeout(function() {
                        Cryptodog.xmpp.reconnect();
                    }, 5000);
                }
            }
        });
    };

    // Handle incoming messages from the XMPP server.
    Cryptodog.xmpp.onMessage = function(message) {
        var nickname = extractNickname($(message).attr('from'));

        var body = $(message)
            .find('body')
            .text();

        var type = $(message).attr('type');

        // If archived message, ignore.
        if ($(message).find('delay').length !== 0) {
            return true;
        }

        // If message is from me, ignore.
        if (nickname === Cryptodog.me.nickname) {
            return true;
        }

        // If message is from someone not on buddy list, ignore.
        if (!Cryptodog.buddies.hasOwnProperty(nickname)) {
            return true;
        }

        // Check if message has a 'composing' notification.
        if ($(message).find('composing').length && !body.length) {
            $('#buddy-' + Cryptodog.buddies[nickname].id).addClass('composing');
            return true;
        }

        // Check if message has a 'paused' (stopped writing) notification.
        if ($(message).find('paused').length) {
            $('#buddy-' + Cryptodog.buddies[nickname].id).removeClass('composing');
        } else if (type === 'groupchat' && body.length) {
            // Check if message is a group chat message.
            $('#buddy-' + Cryptodog.buddies[nickname].id).removeClass('composing');

            body = Cryptodog.multiParty.receiveMessage(nickname, Cryptodog.me.nickname, body);

            if (typeof body === 'string') {
                Cryptodog.addToConversation(body, nickname, 'groupChat', 'message');
            }
        } else if (type === 'chat') {
            // Check if this is a private OTR message.
            $('#buddy-' + Cryptodog.buddies[nickname].id).removeClass('composing');

            Cryptodog.buddies[nickname].otr.receiveMsg(body);
        }
        return true;
    };

    // Handle incoming presence updates from the XMPP server.
    Cryptodog.xmpp.onPresence = function(presence) {
        var status;
        var nickname = extractNickname($(presence).attr('from'));

        // If invalid nickname, do not process.
        if ($(presence).attr('type') === 'error') {
            if (
                $(presence)
                    .find('error')
                    .attr('code') === '409'
            ) {
                // Delay logout in order to avoid race condition with window animation.
                window.setTimeout(function() {
                    Cryptodog.logout();
                    Cryptodog.UI.loginFail(Cryptodog.locale['loginMessage']['nicknameInUse']);
                }, 3000);

                return false;
            }
            return true;
        }

        // Ignore if presence status is coming from myself.
        if (nickname === Cryptodog.me.nickname) {
            return true;
        }

        // Detect nickname change (which may be done by non-Cryptodog XMPP clients).
        if (
            $(presence)
                .find('status')
                .attr('code') === '303'
        ) {
            Cryptodog.removeBuddy(nickname);
            return true;
        }

        // Detect buddy going offline.
        if ($(presence).attr('type') === 'unavailable') {
            Cryptodog.removeBuddy(nickname);
            return true;
        } else if (!Cryptodog.buddies.hasOwnProperty(nickname)) {
            // Create buddy element if buddy is new
            Cryptodog.addBuddy(nickname, null, 'online');

            for (var i = 0; i < 4000; i += 2000) {
                window.setTimeout(Cryptodog.xmpp.sendPublicKey, i, nickname);
            }

            // Propagate away status to newcomers.
            Cryptodog.xmpp.sendStatus();
        } else if (
            $(presence)
                .find('show')
                .text() === '' ||
            $(presence)
                .find('show')
                .text() === 'chat'
        ) {
            status = 'online';
        } else {
            status = 'away';
        }

        Cryptodog.buddyStatus(nickname, status);
        return true;
    };

    // Send your own multiparty public key to `nickname`, via XMPP-MUC.
    Cryptodog.xmpp.sendPublicKey = function(nickname) {
        Cryptodog.xmpp.connection.muc.message(
            Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
            null,
            Cryptodog.multiParty.sendPublicKey(nickname),
            null,
            'groupchat',
            'active'
        );
    };

    // Send your current status to the XMPP server.
    Cryptodog.xmpp.sendStatus = function() {
        var status = '';

        if (Cryptodog.xmpp.currentStatus === 'away') {
            status = 'away';
        }

        Cryptodog.xmpp.connection.muc.setStatus(
            Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
            Cryptodog.me.nickname,
            status,
            status
        );
    };

    // Executed (manually) after connection.
    var afterConnect = function() {
        $('.conversationName').animate({ 'background-color': '#bb7a20' });

        Cryptodog.xmpp.connection.ibb.addIBBHandler(Cryptodog.otr.ibbHandler);
        Cryptodog.xmpp.connection.si_filetransfer.addFileHandler(Cryptodog.otr.fileHandler);

        Cryptodog.xmpp.sendStatus();
    };

    // Extract nickname (part after forward slash) from JID
    var extractNickname = function(from) {
        var name = from.match(/\/([\s\S]+)/);

        if (name) {
            return name[1];
        }
        return false;
    };
});
<|endoftext|>


<commit_before>$(function() {

    var farmers = [],
        markers = [],
        countryLegend = {},
        rotation;

    var currentInfo = $('#side-menu ul'),
        body = $('body');

    var earth = new WE.map('earth_div', {
        sky: true,
        atmosphere: true
    });

    // This line adds support for MapQuest Aerial tiles
    WE.tileLayer('http://otile2.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg', {
        attribution: 'Tiles Courtesy of MapQuest.'
    }).addTo(earth);

    // Start a rotation animation
    rotation = setInterval(function() {
        var c = earth.getPosition();
        if (c[0] && c[1]) {
            earth.setCenter([c[0], c[1] + 0.1]);
        }
    }, 30);

    // Opening and closing the side menu
    $('#open-side-menu').on('click', function() {
        body.toggleClass('open');
    });

    // This function sends an ajax get request to /online,
    // fetches a list of farmers and coordinates
    // then renders the globe
    var getData = function() {
        $.ajax({
            url: 'http://verify.driveshare.org/api/downstream/status/list/by/d/uptime',
            success: function(data) {
                farmers = data.farmers;
                generateLegend(farmers);
                render();
            }
        });
    }

    // Fetch data for the globe once initially,
    // and then every 5 seconds.
    getData();
    setInterval(getData, 5000);

    // This function returns a random hex color.
    var randomColor = function() {
        return '#' + (Math.random().toString(16) + '0000000').slice(2, 8);
    }

    // Maps all the countries of farmers to a randomly defined color.
    var generateLegend = function(farmers) {
        var country, j;
        for (j = 0; j < farmers.length; j++) {
            country = farmers[j].location.country;
            if (!countryLegend.hasOwnProperty(country)) {
                countryLegend[country] = randomColor();
            }
        }
    }

    // This function handles everything related to updating the HTML
    // with the returned data from the AJAX call:
    var render = function() {
        var entry;
        if (!farmers.length) {

            // If there isn't anybody online
            // Show an appropriate message and empty the summary and the list section
            currentInfo.empty();
            $('#no-members').show();
            $('.summary').html('');

        } else {
            // If there are people online:
            // On every call clear the side-menu list and globe markers and fill them up with new info.
            currentInfo.empty();
            $('#no-members').hide();
            for (var i = 0; i < 10; i++) {
                // Populate the side-menu with the farmers, ranked by uptime.
                currentInfo.append($('

'));
                entry = $('#farmer' + (i + 1));
                entry.append($('').css('background-color', countryLegend[farmers[i].location.country]));
                entry.append($('
' + farmers[i].address + '
'));
                entry.append($('' + farmers[i].uptime + '%'));

            }

            // Summary information
            $('.summary').html(farmers.length + ' farmers / ' + Object.keys(countryLegend).length + ' countries');
        }

        // Calculate number of side-menu entries depending on screen size.
        showEntries();
        $(window).resize(showEntries);

        // Adding the markers to the globe.

        // First remove all existing markers from the globe and clear the array they are stored in.
        markers.forEach(function(b) {
            earth.removeMarker(b);
        });
        markers = [];

        // For each ip location create a new marker
        farmers.forEach(function(a) {
        	var country = a.location.country;
        	var city = a.location.city;
        	var state = a.location.state;
        	var numContracts = a.contracts;
        	var heartbeats = a.heartbeats;
        	var testFileSize = a.size;

            markers.push(WE.marker([a.location.lat, a.location.lon]).addTo(earth).bindPopup('This farmer from ' + (city ? city : country) + '' + (state ? ', ' + state + '' : '') + ' has ' + numContracts + ' contract(s) and passed ' + heartbeats + ' heartbeats with ' + testFileSize + '-byte test files.', {
                maxWidth: 150,
                maxHeight: 100,
                closeButton: true
            }));

            // Color the markers depending on the country they are in.
            $(markers[markers.length - 1].element.firstChild).css('background', countryLegend[a.location.country]);
        });

        // Rotate the globe to the desired country from the list
        $('.entry').on('click', function() {
            if ($(window).width() < 800) {
                body.removeClass('open');
            }
            clearInterval(rotation);
            var index = $(this).attr('id').split('farmer')[1] - 1;
            earth.panTo([farmers[index].location.lat, farmers[index].location.lon]);
        });

    }

    $('#earth_div').on('click touchstart touchend', function() {
        // Stop the rotation when the earth is clicked or touched
        clearInterval(rotation);

        // If the screen is small close the side menu as well.
        if ($(window).width() < 800) {
            body.removeClass('open');
        }
    });

    // Shows only a part of the entries depending on the screen size
    var showEntries = function() {
        var screenHeight = $(window).height(),
            sideMenuEntriesSpace = screenHeight - 250, // Exclude the title and the summary height
            numberOfEntries = Math.round(sideMenuEntriesSpace / 55); // 55 is the height of an entry in pixels

        // The list of countries on the sidebar
        var countries = $('#side-menu .entry');

        // Hide all countries
        countries.hide();

        // Show only as many countries as would fit on the window
        countries.slice(0, numberOfEntries).show();
    }

    // Open the side menu on start up if the app is running on a big screen.
    if ($(window).width() > 800) {
        body.addClass('open');
    } else {
        earth.setZoom(1.8);
    }

});
<commit_msg>Tweaks.<commit_after>$(function() {

    var farmers = [],
        markers = [],
        countryLegend = {},
        rotation;

    var currentInfo = $('#side-menu ul'),
        body = $('body');

    var earth = new WE.map('earth_div', {
        sky: true,
        atmosphere: true
    });

    // This line adds support for MapQuest Aerial tiles
    WE.tileLayer('http://otile2.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg', {
        attribution: 'Tiles Courtesy of MapQuest.'
    }).addTo(earth);

    // Start a rotation animation
    rotation = setInterval(function() {
        var c = earth.getPosition();
        if (c[0] && c[1]) {
            earth.setCenter([c[0], c[1] + 0.1]);
        }
    }, 30);

    // Opening and closing the side menu
    $('#open-side-menu').on('click', function() {
        body.toggleClass('open');
    });

    // This function sends an ajax get request to /online,
    // fetches a list of farmers and coordinates
    // then renders the globe
    var getData = function() {
        $.ajax({
            url: 'http://verify.driveshare.org/api/downstream/status/list/by/d/uptime',
            success: function(data) {
                farmers = data.farmers.filter(function(farmer) {
                	return farmer.uptime > 0;
                });
                generateLegend(farmers);
                render();
            }
        });
    }

    // Fetch data for the globe once initially,
    // and then every 5 seconds.
    getData();
    setInterval(getData, 5000);

    // This function returns a random hex color.
    var randomColor = function() {
        return '#' + (Math.random().toString(16) + '0000000').slice(2, 8);
    }

    // Maps all the countries of farmers to a randomly defined color.
    var generateLegend = function(farmers) {
        var country, j;
        for (j = 0; j < farmers.length; j++) {
            country = farmers[j].location.country;
            if (!countryLegend.hasOwnProperty(country)) {
                countryLegend[country] = randomColor();
            }
        }
    }

    // This function handles everything related to updating the HTML
    // with the returned data from the AJAX call:
    var render = function() {
        var entry;
        if (!farmers.length) {

            // If there isn't anybody online
            // Show an appropriate message and empty the summary and the list section
            currentInfo.empty();
            $('#no-members').show();
            $('.summary').html('');

        } else {
            // If there are people online:
            // On every call clear the side-menu list and globe markers and fill them up with new info.
            currentInfo.empty();
            $('#no-members').hide();
            for (var i = 0; i < 10; i++) {
                // Populate the side-menu with the farmers, ranked by uptime.
                currentInfo.append($('

'));
                entry = $('#farmer' + (i + 1));
                entry.append($('').css('background-color', countryLegend[farmers[i].location.country]));
                entry.append($('
' + farmers[i].address + '
'));
                entry.append($('' + Math.round(farmers[i].uptime) + '%'));

            }

            // Summary information
            $('.summary').html(farmers.length + ' farmers / ' + Object.keys(countryLegend).length + ' countries');
        }

        // Calculate number of side-menu entries depending on screen size.
        showEntries();
        $(window).resize(showEntries);

        // Adding the markers to the globe.

        // First remove all existing markers from the globe and clear the array they are stored in.
        markers.forEach(function(b) {
            earth.removeMarker(b);
        });
        markers = [];

        // For each ip location create a new marker
        farmers.forEach(function(a) {
        	var country = a.location.country;
        	var city = a.location.city;
        	var state = a.location.state;
        	var numContracts = a.contracts;
        	var heartbeats = a.heartbeats;
        	var testFileSize = a.size;

            markers.push(WE.marker([a.location.lat, a.location.lon]).addTo(earth).bindPopup('This farmer from ' + (city ? city : country) + '' + (state ? ', ' + state + '' : '') + ' has ' + numContracts + ' contract(s) and passed ' + heartbeats + ' heartbeats with ' + testFileSize + '-byte test files.', {
                maxWidth: 150,
                maxHeight: 100,
                closeButton: true
            }));

            // Color the markers depending on the country they are in.
            $(markers[markers.length - 1].element.firstChild).css('background', countryLegend[a.location.country]);
        });

        // Rotate the globe to the desired farmer from the list
        $('.entry').on('click', function() {
            if ($(window).width() < 800) {
                body.removeClass('open');
            }
            clearInterval(rotation);
            var index = $(this).attr('id').split('farmer')[1] - 1;
            earth.panTo([farmers[index].location.lat, farmers[index].location.lon]);
        });

    }

    $('#earth_div').on('click touchstart touchend', function() {
        // Stop the rotation when the earth is clicked or touched
        clearInterval(rotation);

        // If the screen is small close the side menu as well.
        if ($(window).width() < 800) {
            body.removeClass('open');
        }
    });

    // Shows only a part of the entries depending on the screen size
    var showEntries = function() {
        var screenHeight = $(window).height(),
            sideMenuEntriesSpace = screenHeight - 250, // Exclude the title and the summary height
            numberOfEntries = Math.round(sideMenuEntriesSpace / 55); // 55 is the height of an entry in pixels

        // The list of countries on the sidebar
        var countries = $('#side-menu .entry');

        // Hide all countries
        countries.hide();

        // Show only as many countries as would fit on the window
        countries.slice(0, numberOfEntries).show();
    }

    // Open the side menu on start up if the app is running on a big screen.
    if ($(window).width() > 800) {
        body.addClass('open');
    } else {
        earth.setZoom(1.8);
    }

});
<|endoftext|>


<commit_before>import _ from 'underscore';

import AccessWidget from 'girder/views/widgets/AccessWidget';
import View from 'girder/views/View';
import { AccessType } from 'girder/constants';

import saveAnnotation from '../templates/dialogs/saveAnnotation.pug';
import '../stylesheets/dialogs/saveAnnotation.styl';

/**
 * Create a modal dialog with fields to edit the properties of
 * an annotation before POSTing it to the server.
 */
var SaveAnnotation = View.extend({
    events: {
        'click .h-access': 'access',
        'click .h-cancel': 'cancel',
        'submit form': 'save'
    },

    render() {
        this.$el.html(
            saveAnnotation({
                title: this.options.title,
                hasAdmin: this.annotation.get('_accessLevel') >= AccessType.ADMIN,
                annotation: this.annotation.toJSON().annotation
            })
        ).girderModal(this);
        return this;
    },

    access(evt) {
        evt.preventDefault();
        new AccessWidget({
            el: $('#g-dialog-container'),
            type: 'annotation',
            hideRecurseOption: true,
            parentView: this,
            model: this.annotation
        }).on('g:accessListSaved', () => {
            this.annotation.fetch();
        });
    },

    cancel(evt) {
        evt.preventDefault();
        this.$el.modal('hide');
    },

    /**
     * Respond to form submission.  Triggers a `g:save` event on the
     * AnnotationModel.
     */
    save(evt) {
        evt.preventDefault();
        if (!this.$('#h-annotation-name').val()) {
            this.$('#h-annotation-name').parent()
                .addClass('has-error');
            this.$('.g-validation-failed-message')
                .text('Please enter a name.')
                .removeClass('hidden');
            return;
        }

        _.extend(this.annotation.get('annotation'), {
            name: this.$('#h-annotation-name').val(),
            description: this.$('#h-annotation-description').val()
        });
        this.trigger('g:submit');
        this.$el.modal('hide');
    }
});

/**
 * Create a singleton instance of this widget that will be rendered
 * when `show` is called.
 */
var dialog = new SaveAnnotation({
    parentView: null
});

/**
 * Show the save dialog box.  Watch for the `g:submit` event on the
 * view to respond to user submission of the form.
 *
 * @param {AnnotationModel} annotationElement The element to edit
 * @returns {SaveAnnotation} The dialog's view
 */
function show(annotation, options) {
    _.defaults(options, {'title': 'Create annotation'});
    dialog.annotation = annotation;
    dialog.options = options;
    dialog.setElement('#g-dialog-container').render();
    return dialog;
}

export default show;
<commit_msg>Fix triggering updates in the AccessWidget<commit_after>import _ from 'underscore';

import AccessWidget from 'girder/views/widgets/AccessWidget';
import View from 'girder/views/View';
import { AccessType } from 'girder/constants';

import saveAnnotation from '../templates/dialogs/saveAnnotation.pug';
import '../stylesheets/dialogs/saveAnnotation.styl';

/**
 * Create a modal dialog with fields to edit the properties of
 * an annotation before POSTing it to the server.
 */
var SaveAnnotation = View.extend({
    events: {
        'click .h-access': 'access',
        'click .h-cancel': 'cancel',
        'submit form': 'save'
    },

    render() {
        this.$el.html(
            saveAnnotation({
                title: this.options.title,
                hasAdmin: this.annotation.get('_accessLevel') >= AccessType.ADMIN,
                annotation: this.annotation.toJSON().annotation
            })
        ).girderModal(this);
        return this;
    },

    access(evt) {
        evt.preventDefault();
        this.annotation.off('g:accessListSaved');
        new AccessWidget({
            el: $('#g-dialog-container'),
            type: 'annotation',
            hideRecurseOption: true,
            parentView: this,
            model: this.annotation
        }).on('g:accessListSaved', () => {
            this.annotation.fetch();
        });
    },

    cancel(evt) {
        evt.preventDefault();
        this.$el.modal('hide');
    },

    /**
     * Respond to form submission.  Triggers a `g:save` event on the
     * AnnotationModel.
     */
    save(evt) {
        evt.preventDefault();
        if (!this.$('#h-annotation-name').val()) {
            this.$('#h-annotation-name').parent()
                .addClass('has-error');
            this.$('.g-validation-failed-message')
                .text('Please enter a name.')
                .removeClass('hidden');
            return;
        }

        _.extend(this.annotation.get('annotation'), {
            name: this.$('#h-annotation-name').val(),
            description: this.$('#h-annotation-description').val()
        });
        this.trigger('g:submit');
        this.$el.modal('hide');
    }
});

/**
 * Create a singleton instance of this widget that will be rendered
 * when `show` is called.
 */
var dialog = new SaveAnnotation({
    parentView: null
});

/**
 * Show the save dialog box.  Watch for the `g:submit` event on the
 * view to respond to user submission of the form.
 *
 * @param {AnnotationModel} annotationElement The element to edit
 * @returns {SaveAnnotation} The dialog's view
 */
function show(annotation, options) {
    _.defaults(options, {'title': 'Create annotation'});
    dialog.annotation = annotation;
    dialog.options = options;
    dialog.setElement('#g-dialog-container').render();
    return dialog;
}

export default show;
<|endoftext|>


<commit_before>/*jslint browser:true */
/*global socSortCorrelation, SocReport, $*/

Date.prototype.format=function(format){var returnStr='';var replace=Date.replaceChars;for(var i=0;i<format.length;i++){var curChar=format.charAt(i);if(replace[curChar]){returnStr+=replace[curChar].call(this);}else{returnStr+=curChar;}}return returnStr;};Date.replaceChars={shortMonths:['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],longMonths:['January','February','March','April','May','June','July','August','September','October','November','December'],shortDays:['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],longDays:['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],d:function(){return(this.getDate()<10?'0':'')+this.getDate();},D:function(){return Date.replaceChars.shortDays[this.getDay()];},j:function(){return this.getDate();},l:function(){return Date.replaceChars.longDays[this.getDay()];},N:function(){return this.getDay()+1;},S:function(){return(this.getDate()%10==1&&this.getDate()!=11?'st':(this.getDate()%10==2&&this.getDate()!=12?'nd':(this.getDate()%10==3&&this.getDate()!=13?'rd':'th')));},w:function(){return this.getDay();},z:function(){return"Not Yet Supported";},W:function(){return"Not Yet Supported";},F:function(){return Date.replaceChars.longMonths[this.getMonth()];},m:function(){return(this.getMonth()<9?'0':'')+(this.getMonth()+1);},M:function(){return Date.replaceChars.shortMonths[this.getMonth()];},n:function(){return this.getMonth()+1;},t:function(){return"Not Yet Supported";},L:function(){return(((this.getFullYear()%4==0)&&(this.getFullYear()%100!=0))||(this.getFullYear()%400==0))?'1':'0';},o:function(){return"Not Supported";},Y:function(){return this.getFullYear();},y:function(){return(''+this.getFullYear()).substr(2);},a:function(){return this.getHours()<12?'am':'pm';},A:function(){return this.getHours()<12?'AM':'PM';},B:function(){return"Not Yet Supported";},g:function(){return this.getHours()%12||12;},G:function(){return this.getHours();},h:function(){return((this.getHours()%12||12)<10?'0':'')+(this.getHours()%12||12);},H:function(){return(this.getHours()<10?'0':'')+this.getHours();},i:function(){return(this.getMinutes()<10?'0':'')+this.getMinutes();},s:function(){return(this.getSeconds()<10?'0':'')+this.getSeconds();},e:function(){return"Not Yet Supported";},I:function(){return"Not Supported";},O:function(){return(-this.getTimezoneOffset()<0?'-':'+')+(Math.abs(this.getTimezoneOffset()/60)<10?'0':'')+(Math.abs(this.getTimezoneOffset()/60))+'00';},P:function(){return(-this.getTimezoneOffset()<0?'-':'+')+(Math.abs(this.getTimezoneOffset()/60)<10?'0':'')+(Math.abs(this.getTimezoneOffset()/60))+':'+(Math.abs(this.getTimezoneOffset()%60)<10?'0':'')+(Math.abs(this.getTimezoneOffset()%60));},T:function(){var m=this.getMonth();this.setMonth(0);var result=this.toTimeString().replace(/^.+ \(?([^\)]+)\)?$/,'$1');this.setMonth(m);return result;},Z:function(){return-this.getTimezoneOffset()*60;},c:function(){return this.format("Y-m-d")+"T"+this.format("H:i:sP");},r:function(){return this.toString();},U:function(){return this.getTime()/1000;}};

$(document).ready(function () {
    var shouldLoadCPU = true;
    $('#report-list-nav li a').click(function () {
        if (shouldLoadCPU) {
            shouldLoadCPU = false;
            $.map(['cpu', 'addon', 'module'], function(type) {
                $('#' + type + '_correlation').load(SocReport.base + type + SocReport.path,
                  function (response, status) {
                    if (status == "error") {
                        $('#' + type + '_correlation').html('error, could not load data');
                    } else {
                        socSortCorrelation('#' + type + '_correlation');
                    }
                });
            });
        }
    });
    $('button.load-version-data').click(function () {
        var t = $(this).attr('name');
        $('#' + t + '-panel').html(SocReport.loading).load(SocReport.base + t + SocReport.path, function(response, status){
            if (status == "error") {
                $('#' + t + '-panel').html('error, could not load data');
            }
        });
    });

    $('#reportsList .hang-pair-btn').click(function() {
        var tr = $(this).parent().parent().parent();

        var url = $('input.ajax_endpoint', tr).val();

        $('.hang-pair', tr).html("");
        $.getJSON(url, function(data) {
            if (data.length > 0 ) {
	        for (var i=0; data.length; i++) {
	            var hangType = data[i].processType && data[i].processType == 'plugin' ? 'Plugin' : 'Browser';
	            $('.hang-pair', tr).html(hangType + " Hang:
" + data[i].display_date_processed  + "");
                    $('img', tr).unbind('click');
	            break;
                }
            } else {
                $('.hang-pair', tr).html("Unable to locate other Hang Part.");
            }
        });
        return false;
    });

    $('#buildid-table').tablesorter();

    $.tablesorter.addParser({
        id: "hexToInt",
        is: function(s) {
            return false;
        },
        format: function(s) {
            return parseInt(s, 16);
        },
        type: "digit"
    });

    $('#reportsList').tablesorter({
        textExtraction: "complex",
        headers: {
            7: { sorter: "hexToInt" }
        },
        sortList : [[9,1]]
    });

    $('#report-list-nav').tabs({selected: 2}).show();
});
<commit_msg>#573378 - uptime does not sort numerically<commit_after>/*jslint browser:true */
/*global socSortCorrelation, SocReport, $*/

Date.prototype.format=function(format){var returnStr='';var replace=Date.replaceChars;for(var i=0;i<format.length;i++){var curChar=format.charAt(i);if(replace[curChar]){returnStr+=replace[curChar].call(this);}else{returnStr+=curChar;}}return returnStr;};Date.replaceChars={shortMonths:['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],longMonths:['January','February','March','April','May','June','July','August','September','October','November','December'],shortDays:['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],longDays:['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],d:function(){return(this.getDate()<10?'0':'')+this.getDate();},D:function(){return Date.replaceChars.shortDays[this.getDay()];},j:function(){return this.getDate();},l:function(){return Date.replaceChars.longDays[this.getDay()];},N:function(){return this.getDay()+1;},S:function(){return(this.getDate()%10==1&&this.getDate()!=11?'st':(this.getDate()%10==2&&this.getDate()!=12?'nd':(this.getDate()%10==3&&this.getDate()!=13?'rd':'th')));},w:function(){return this.getDay();},z:function(){return"Not Yet Supported";},W:function(){return"Not Yet Supported";},F:function(){return Date.replaceChars.longMonths[this.getMonth()];},m:function(){return(this.getMonth()<9?'0':'')+(this.getMonth()+1);},M:function(){return Date.replaceChars.shortMonths[this.getMonth()];},n:function(){return this.getMonth()+1;},t:function(){return"Not Yet Supported";},L:function(){return(((this.getFullYear()%4==0)&&(this.getFullYear()%100!=0))||(this.getFullYear()%400==0))?'1':'0';},o:function(){return"Not Supported";},Y:function(){return this.getFullYear();},y:function(){return(''+this.getFullYear()).substr(2);},a:function(){return this.getHours()<12?'am':'pm';},A:function(){return this.getHours()<12?'AM':'PM';},B:function(){return"Not Yet Supported";},g:function(){return this.getHours()%12||12;},G:function(){return this.getHours();},h:function(){return((this.getHours()%12||12)<10?'0':'')+(this.getHours()%12||12);},H:function(){return(this.getHours()<10?'0':'')+this.getHours();},i:function(){return(this.getMinutes()<10?'0':'')+this.getMinutes();},s:function(){return(this.getSeconds()<10?'0':'')+this.getSeconds();},e:function(){return"Not Yet Supported";},I:function(){return"Not Supported";},O:function(){return(-this.getTimezoneOffset()<0?'-':'+')+(Math.abs(this.getTimezoneOffset()/60)<10?'0':'')+(Math.abs(this.getTimezoneOffset()/60))+'00';},P:function(){return(-this.getTimezoneOffset()<0?'-':'+')+(Math.abs(this.getTimezoneOffset()/60)<10?'0':'')+(Math.abs(this.getTimezoneOffset()/60))+':'+(Math.abs(this.getTimezoneOffset()%60)<10?'0':'')+(Math.abs(this.getTimezoneOffset()%60));},T:function(){var m=this.getMonth();this.setMonth(0);var result=this.toTimeString().replace(/^.+ \(?([^\)]+)\)?$/,'$1');this.setMonth(m);return result;},Z:function(){return-this.getTimezoneOffset()*60;},c:function(){return this.format("Y-m-d")+"T"+this.format("H:i:sP");},r:function(){return this.toString();},U:function(){return this.getTime()/1000;}};

$(document).ready(function () {
    var shouldLoadCPU = true;
    $('#report-list-nav li a').click(function () {
        if (shouldLoadCPU) {
            shouldLoadCPU = false;
            $.map(['cpu', 'addon', 'module'], function(type) {
                $('#' + type + '_correlation').load(SocReport.base + type + SocReport.path,
                  function (response, status) {
                    if (status == "error") {
                        $('#' + type + '_correlation').html('error, could not load data');
                    } else {
                        socSortCorrelation('#' + type + '_correlation');
                    }
                });
            });
        }
    });
    $('button.load-version-data').click(function () {
        var t = $(this).attr('name');
        $('#' + t + '-panel').html(SocReport.loading).load(SocReport.base + t + SocReport.path, function(response, status){
            if (status == "error") {
                $('#' + t + '-panel').html('error, could not load data');
            }
        });
    });

    $('#reportsList .hang-pair-btn').click(function() {
        var tr = $(this).parent().parent().parent();

        var url = $('input.ajax_endpoint', tr).val();

        $('.hang-pair', tr).html("");
        $.getJSON(url, function(data) {
            if (data.length > 0 ) {
	        for (var i=0; data.length; i++) {
	            var hangType = data[i].processType && data[i].processType == 'plugin' ? 'Plugin' : 'Browser';
	            $('.hang-pair', tr).html(hangType + " Hang:
" + data[i].display_date_processed  + "");
                    $('img', tr).unbind('click');
	            break;
                }
            } else {
                $('.hang-pair', tr).html("Unable to locate other Hang Part.");
            }
        });
        return false;
    });

    $('#buildid-table').tablesorter();

    $.tablesorter.addParser({
        id: "hexToInt",
        is: function(s) {
            return false;
        },
        format: function(s) {
            return parseInt(s, 16);
        },
        type: "digit"
    });

    $('#reportsList').tablesorter({
        textExtraction: "complex",
        headers: {
            7: { sorter: "hexToInt" },  // Address
            9: { sorter: "digit" }      // Uptime
        },
        sortList : [[10,1]]
    });

    $('#report-list-nav').tabs({selected: 2}).show();
});
<|endoftext|>


<commit_before>(function ($, window, document) {
    "use strict";
    var ovoplayer = {};
    ovoplayer.load_script = [];

    /**
    @method load_script
    @param type {youtube|dailymotion|vimeo}
    */
    var load_script = function (type) {
        var e, s, url;
        if (ovoplayer.load_script[type]) {
            return;
        } else {
            ovoplayer.load_script[type] = true;
        }
        type = type || 'youtube';
        switch (type) {
            case 'youtube':
                url = 'https://www.youtube.com/iframe_api';
                break;
            case 'dailymotion':
                url = document.location.protocol + '//api.dmcdn.net/all.js';
                break;
            case 'vimeo':
                url = 'http://a.vimeocdn.com/js/froogaloop2.min.js?938a9-1384184538';
                break;
        }
        e = document.createElement('script');
        e.src = url;
        e.async = true;
        s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(e, s);
    };

    var set_current_data = function () {
        ovoplayer.current = $.fn.ovoplayer.settings;
    }

    $.fn.ovoplayer = function (settings) {
        var self = this;
        ovoplayer.settings = settings;
        $.fn.ovoplayer.init(settings);
    };

    $.fn.ovoplayer.update = function (settings) {
        var params, iframe, o = $.fn.ovoplayer.settings = $.extend({}, $.fn.ovoplayer.defaults, ovoplayer.settings, settings);
        if (!ovoplayer.load_script[o.type]) {
            return $.fn.ovoplayer.init(o);
        }
        if (ovoplayer.item) {
            if (ovoplayer.current.type == 'youtube') {
                ovoplayer.item.destroy();
                ovoplayer.item = undefined;
            }
        }
        switch (o.type) {
            case 'youtube':
                ovoplayer.item = new YT.Player(o.frame_id.youtube, {
                    width: o.width,
                    height: o.height,
                    videoId: o.code,
                    events: {
                        'onReady': onPlayerReady
                    }
                });

                window.onPlayerReady = function(e) {
                    if (o.autoplay) {
                        e.target.playVideo();
                    }
                }
                break;
            case 'dailymotion':
                if (ovoplayer.current.type == o.type) {
                    ovoplayer.item.load(o.code);
                } else {
                    // PARAMS is a javascript object containing parameters to pass to the player if any (eg: {autoplay: 1})
                    params = (o.autoplay) ? {autoplay: 1} : {};
                    ovoplayer.item = DM.player(o.vimeo.dailymotion, {video: o.code, width: o.width, height: o.height, params: params});

                    // 4. We can attach some events on the player (using standard DOM events)
                    ovoplayer.item.addEventListener("apiready", function(e) {
                        e.target.play();
                    });
                }
                break;
            case 'vimeo':
                iframe = '';
                $('#' + o.frame_id.dailymotion).html(iframe);
                window.addEvent = function(element, eventName, callback) {
                    if (element.addEventListener) {
                        element.addEventListener(eventName, callback, false);
                    }
                    else {
                        element.attachEvent('on' + eventName, callback);
                    }
                }
                window.ready = function(player_id) {
                    // Keep a reference to Froogaloop for this player
                    ovoplayer.item = $f(player_id);
                    ovoplayer.item.api('play');
                };

                window.addEventListener('load', function() {
                    // Attach the ready event to the iframe
                    $f(document.getElementById(o.vimeoPlayer)).addEvent('ready', ready);
                });
                break;
        }
        set_current_data();
    };

    $.fn.ovoplayer.init = function(options) {
        // Apply any options to the settings, override the defaults
        var params, iframe, o = $.fn.ovoplayer.settings = $.extend({}, $.fn.ovoplayer.defaults, options);
        // insert video frame
        $.each(o.frame_id, function(key, value) {
            $('
', {
                id: value
            }).appendTo('#' + o.id);
        });
        // load third party script.
        load_script(o.type);
        switch (o.type) {
            case 'youtube':
                window.onYouTubeIframeAPIReady = function () {
                    ovoplayer.item = new YT.Player(o.frame_id.youtube, {
                        width: o.width,
                        height: o.height,
                        videoId: o.code,
                        events: {
                            'onReady': onPlayerReady
                        }
                    });
                };

                window.onPlayerReady = function(e) {
                    if (o.autoplay) {
                        e.target.playVideo();
                    }
                }
                break;
            case 'dailymotion':
                window.dmAsyncInit = function() {
                    // PARAMS is a javascript object containing parameters to pass to the player if any (eg: {autoplay: 1})
                    params = (o.autoplay) ? {autoplay: 1} : {};
                    ovoplayer.item = DM.player(o.frame_id.dailymotion, {video: o.code, width: o.width, height: o.height, params: params});

                    // 4. We can attach some events on the player (using standard DOM events)
                    ovoplayer.item.addEventListener("apiready", function(e) {
                        e.target.play();
                    });
                };
                break;
            case 'vimeo':
                iframe = '';
                $('#' + o.frame_id.vimeo).html(iframe);
                window.addEvent = function(element, eventName, callback) {
                    if (element.addEventListener) {
                        element.addEventListener(eventName, callback, false);
                    }
                    else {
                        element.attachEvent('on' + eventName, callback);
                    }
                }
                window.ready = function(player_id) {
                    // Keep a reference to Froogaloop for this player
                    ovoplayer.item = $f(player_id);
                    ovoplayer.item.api('play');
                };

                window.addEventListener('load', function() {
                    // Attach the ready event to the iframe
                    $f(document.getElementById(o.vimeoPlayer)).addEvent('ready', ready);
                });
                break;
        }
        set_current_data();
    };

    // Defaults
    $.fn.ovoplayer.defaults = {
        id: 'player_frome',
        frame_id: {
            youtube: 'youtube_frame',
            vimeo: 'vimeo_frame',
            dailymotion: 'dailymotion_frame'
        },
        vimeoPlayer: 'vimeo_player',
        width: 640,
        height: 480,
        autoplay: true
    };

    $.ovoplayer = $.fn.ovoplayer;
}(jQuery, window, document));
<commit_msg>add youtube API.<commit_after>(function ($, window, document) {
    "use strict";
    var ovoplayer = {}, player = {};
    ovoplayer.load_script = [];

    ovoplayer.youtube = function() {
        this.is_init = false;
        this.player = undefined;
        this.options = $.fn.ovoplayer.settings;
        this.initialize.apply(this, arguments);
    };

    $.extend(ovoplayer.youtube.prototype, {
        initialize: function(){
            var self = this;
            window.onYouTubeIframeAPIReady = function () {
                self.player = new YT.Player(self.options.frame_id.youtube, {
                    width: self.options.width,
                    height: self.options.height,
                    videoId: self.options.code,
                    events: {
                        'onReady': self.onPlayerReady,
                        'onStateChange': self.onPlayerStateChange
                    }
                });
            };
        },
        onPlayerStateChange: function(e) {
            if (e.data == YT.PlayerState.PLAYING) {
                console.log('Video playing');
            } else {
                console.log('Video has paused');
            }
        },
        onPlayerReady: function(e) {
            if ($.fn.ovoplayer.settings.autoplay) {
                e.target.playVideo();
            }
        },
        stopVideo: function() {
            this.player.stopVideo();
        },
        playVideo: function() {
            this.player.playVideo();
        },
        pauseVideo: function() {
            this.player.pauseVideo();
        },
        seekTo: function(seconds) {
            this.player.playVideo(seconds);
        },
        updateVideo: function(setting) {
            var options = {
                videoId: setting.code,
            };
            if (setting.start) {
                options.startSeconds = setting.start;
            }
            if (setting.end) {
                options.endSeconds = setting.end;
            }
            if (setting.quality) {
                options.suggestedQuality = setting.quality;
            }
            this.player.loadVideoById(options);
        },
        setPlaybackQuality: function(quality) {
            this.player.setPlaybackQuality(quality);
        },
        getPlaybackQuality: function(quality) {
            return this.player.getPlaybackQuality();
        },
        init: function() {
            var e, s, url = 'https://www.youtube.com/iframe_api';
            if (this.is_init) return;
            e = document.createElement('script');
            e.src = url;
            e.async = true;
            s = document.getElementsByTagName('script')[0];
            s.parentNode.insertBefore(e, s);
            this.is_init = true;
        }
    });

    ovoplayer.dailymotion = function() {
        this.is_init = false;
        this.initialize.apply(this, arguments);
    };

    $.extend(ovoplayer.dailymotion.prototype, {
        initialize: function(){

        },
        init: function() {
            var e, s, url = document.location.protocol + '//api.dmcdn.net/all.js';
            if (this.is_init) return;
            e = document.createElement('script');
            e.src = url;
            e.async = true;
            s = document.getElementsByTagName('script')[0];
            s.parentNode.insertBefore(e, s);
            this.is_init = true;
        }
    });

    ovoplayer.vimeo = function() {
        this.is_init = false;
    };

    $.extend(ovoplayer.vimeo.prototype, {
        init: function() {
            var e, s, url = 'http://a.vimeocdn.com/js/froogaloop2.min.js?938a9-1384184538';
            if (this.is_init) return;
            e = document.createElement('script');
            e.src = url;
            e.async = true;
            s = document.getElementsByTagName('script')[0];
            s.parentNode.insertBefore(e, s);
            this.is_init = true;
        }
    });

    /**
    @method load_script
    @param type {youtube|dailymotion|vimeo}
    */
    var load_script = function (type) {
        var e, s, url;
        if (ovoplayer.load_script[type]) {
            return;
        } else {
            ovoplayer.load_script[type] = true;
        }
        type = type || 'youtube';
        switch (type) {
            case 'youtube':
                url = 'https://www.youtube.com/iframe_api';
                break;
            case 'dailymotion':
                url = document.location.protocol + '//api.dmcdn.net/all.js';
                break;
            case 'vimeo':
                url = 'http://a.vimeocdn.com/js/froogaloop2.min.js?938a9-1384184538';
                break;
        }
        e = document.createElement('script');
        e.src = url;
        e.async = true;
        s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(e, s);
    };

    var set_current_data = function () {
        ovoplayer.current = $.fn.ovoplayer.settings;
    }

    $.fn.ovoplayer = function (settings) {
        var self = this;
        ovoplayer.settings = settings;
        $.fn.ovoplayer.init(settings);
    };

    $.fn.ovoplayer.update = function (settings) {
        var params, iframe, o = $.fn.ovoplayer.settings = $.extend({}, $.fn.ovoplayer.defaults, ovoplayer.settings, settings);
        player[o.type].init();
        switch (o.type) {
            case 'youtube':
                player[o.type].updateVideo({
                    code: o.code
                });
                break;
            case 'dailymotion':
                if (ovoplayer.current.type == o.type) {
                    ovoplayer.item.load(o.code);
                } else {
                    // PARAMS is a javascript object containing parameters to pass to the player if any (eg: {autoplay: 1})
                    params = (o.autoplay) ? {autoplay: 1} : {};
                    ovoplayer.item = DM.player(o.vimeo.dailymotion, {video: o.code, width: o.width, height: o.height, params: params});

                    // 4. We can attach some events on the player (using standard DOM events)
                    ovoplayer.item.addEventListener("apiready", function(e) {
                        e.target.play();
                    });
                }
                break;
            case 'vimeo':
                iframe = '';
                $('#' + o.frame_id.dailymotion).html(iframe);
                window.addEvent = function(element, eventName, callback) {
                    if (element.addEventListener) {
                        element.addEventListener(eventName, callback, false);
                    }
                    else {
                        element.attachEvent('on' + eventName, callback);
                    }
                }
                window.ready = function(player_id) {
                    // Keep a reference to Froogaloop for this player
                    ovoplayer.item = $f(player_id);
                    ovoplayer.item.api('play');
                };

                window.addEventListener('load', function() {
                    // Attach the ready event to the iframe
                    $f(document.getElementById(o.vimeoPlayer)).addEvent('ready', ready);
                });
                break;
        }
        set_current_data();
    };

    $.fn.ovoplayer.init = function(options) {
        // Apply any options to the settings, override the defaults
        var ovo = [], params, iframe, o = $.fn.ovoplayer.settings = $.extend({}, $.fn.ovoplayer.defaults, options);
        // insert video frame
        $.each(o.frame_id, function(key, value) {
            $('
', {
                id: value
            }).appendTo('#' + o.id);
            // new video player function
            player[key] = new ovoplayer[key]
        });
        // load third party script.
        player[o.type].init();
        switch (o.type) {
            case 'youtube':
                break;
            case 'dailymotion':
                window.dmAsyncInit = function() {
                    // PARAMS is a javascript object containing parameters to pass to the player if any (eg: {autoplay: 1})
                    params = (o.autoplay) ? {autoplay: 1} : {};
                    ovoplayer.item = DM.player(o.frame_id.dailymotion, {video: o.code, width: o.width, height: o.height, params: params});

                    // 4. We can attach some events on the player (using standard DOM events)
                    ovoplayer.item.addEventListener("apiready", function(e) {
                        e.target.play();
                    });
                };
                break;
            case 'vimeo':
                iframe = '';
                $('#' + o.frame_id.vimeo).html(iframe);
                window.addEvent = function(element, eventName, callback) {
                    if (element.addEventListener) {
                        element.addEventListener(eventName, callback, false);
                    }
                    else {
                        element.attachEvent('on' + eventName, callback);
                    }
                }
                window.ready = function(player_id) {
                    // Keep a reference to Froogaloop for this player
                    ovoplayer.item = $f(player_id);
                    ovoplayer.item.api('play');
                };

                window.addEventListener('load', function() {
                    // Attach the ready event to the iframe
                    $f(document.getElementById(o.vimeoPlayer)).addEvent('ready', ready);
                });
                break;
        }
        set_current_data();
    };

    // Defaults
    $.fn.ovoplayer.defaults = {
        id: 'player_frome',
        frame_id: {
            youtube: 'youtube_frame',
            vimeo: 'vimeo_frame',
            dailymotion: 'dailymotion_frame'
        },
        vimeoPlayer: 'vimeo_player',
        width: 640,
        height: 480,
        autoplay: true
    };

    $.ovoplayer = $.fn.ovoplayer;
}(jQuery, window, document));
<|endoftext|>


<commit_before>var isMac = navigator.platform.toLowerCase().indexOf('mac')>=0;
var isLinux = navigator.platform.toLowerCase().indexOf('linux')>=0;

var keyCodes = {
      BACKSPACE: 8,
      TAB: 9,
      ENTER: 13,
      SHIFT: 16,
      CTRL: 17,
      ALT: 18,
      PAUSE: 19,
      CAPS_LOCK: 20,
      ESCAPE: 27,
      SPACE: 32,
      PAGE_UP: 33,
      PAGE_DOWN: 34,
      END: 35,
      HOME: 36,
      LEFT_ARROW: 37,
      UP_ARROW: 38,
      RIGHT_ARROW: 39,
      DOWN_ARROW: 40,
      INSERT: 45,
      DELETE: 46,
      KEY_0: 48,
      KEY_1: 49,
      KEY_2: 50,
      KEY_3: 51,
      KEY_4: 52,
      KEY_5: 53,
      KEY_6: 54,
      KEY_7: 55,
      KEY_8: 56,
      KEY_9: 57,
      KEY_A: 65,
      KEY_B: 66,
      KEY_C: 67,
      KEY_D: 68,
      KEY_E: 69,
      KEY_F: 70,
      KEY_G: 71,
      KEY_H: 72,
      KEY_I: 73,
      KEY_J: 74,
      KEY_K: 75,
      KEY_L: 76,
      KEY_M: 77,
      KEY_N: 78,
      KEY_O: 79,
      KEY_P: 80,
      KEY_Q: 81,
      KEY_R: 82,
      KEY_S: 83,
      KEY_T: 84,
      KEY_U: 85,
      KEY_V: 86,
      KEY_W: 87,
      KEY_X: 88,
      KEY_Y: 89,
      KEY_Z: 90,
      LEFT_META: 91,
      RIGHT_META: 92,
      SELECT: 93,
      NUMPAD_0: 96,
      NUMPAD_1: 97,
      NUMPAD_2: 98,
      NUMPAD_3: 99,
      NUMPAD_4: 100,
      NUMPAD_5: 101,
      NUMPAD_6: 102,
      NUMPAD_7: 103,
      NUMPAD_8: 104,
      NUMPAD_9: 105,
      MULTIPLY: 106,
      ADD: 107,
      SUBTRACT: 109,
      DECIMAL: 110,
      DIVIDE: 111,
      F1: 112,
      F2: 113,
      F3: 114,
      F4: 115,
      F5: 116,
      F6: 117,
      F7: 118,
      F8: 119,
      F9: 120,
      F10: 121,
      F11: 122,
      F12: 123,
      NUM_LOCK: 144,
      SCROLL_LOCK: 145,
      SEMICOLON: 186,
      EQUALS: 187,
      COMMA: 188,
      DASH: 189,
      PERIOD: 190,
      FORWARD_SLASH: 191,
      GRAVE_ACCENT: 192,
      OPEN_BRACKET: 219,
      BACK_SLASH: 220,
      CLOSE_BRACKET: 221,
      SINGLE_QUOTE: 222
    };

var keyText = {
      8 : "backspace",
      9 : "tab",
      13 : "return",
      16 : "shift",
      17 : "control",
      18 : "alt",
      19 : "pause",
      20 : "caps lock",
      27 : "escape",
      32 : "space",
      33 : "page up",
      34 : "page down",
      35 : "end",
      36 : "home",
      37 : "left",
      38 : "up",
      39 : "right",
      40 : "down",
      45 : "insert",
      46 : "delete",
      48 : "0",
      49 : "1",
      50 : "2",
      51 : "3",
      52 : "4",
      53 : "5",
      54 : "6",
      55 : "7",
      56 : "8",
      57 : "9",
      65 : "a",
      66 : "b",
      67 : "c",
      68 : "d",
      69 : "e",
      70 : "f",
      71 : "g",
      72 : "h",
      73 : "i",
      74 : "j",
      75 : "k",
      76 : "l",
      77 : "m",
      78 : "n",
      79 : "o",
      80 : "p",
      81 : "q",
      82 : "r",
      83 : "s",
      84 : "t",
      85 : "u",
      86 : "v",
      87 : "w",
      88 : "x",
      89 : "y",
      90 : "z",
      91 : "left " + (isMac ? "⌘" : isLinux ? "meta" : "windows"),
      92 : "right " + (isMac ? "⌘" : isLinux ? "meta" : "windows"),
      93 : "select",
      96 : "numpad 0",
      97 : "numpad 1",
      98 : "numpad 2",
      99 : "numpad 3",
      100 : "numpad 4",
      101 : "numpad 5",
      102 : "numpad 6",
      103 : "numpad 7",
      104 : "numpad 8",
      105 : "numpad 9",
      106 : "*",
      107 :  "+",
      109 : "-",
      110 : ".",
      111 : "/",
      112 : "F1",
      113 : "F2",
      114 : "F3",
      115 : "F4",
      116 : "F5",
      117 : "F6",
      118 : "F7",
      119 : "F8",
      120 : "F9",
      121 : "F10",
      122 : "F11",
      123 : "F12",
      144 : "numlock",
      145 : "scroll lock",
      186 : ";",
      187 : "=",
      188 : ",",
      189 : "-",
      190 : ".",
      191 : "\/",
      192 : "`",
      219 : "{",
      220 : "\\",
      221 : "}",
      222 : "'"
    };
<commit_msg>Added right analog stick support<commit_after><|endoftext|>