<commit_before>/*

Jappix - An open social platform
These are the music JS scripts for Jappix

-------------------------------------------------

License: AGPL
Author: Valérian Saliou
Contact: http://project.jappix.com/contact
Last revision: 06/11/10

*/

// Opens the music bubble
function openMusic() {
	var path = '.music-content';

	// Show the music bubble
	showBubble(path);
	$(path + ' input').focus();
}

// Parses the music search XML
function parseMusic(xml, type) {
	var path = '.music-content ';
	var content = path + '.list';
	var path_type = content + ' .' + type;

	// Create the result container
	if(!exists(path_type)) {
		var code = '

';

		if(type == 'local')
			$(content).prepend(code);
		else
			$(content).append(code);
	}

	// Fill the results
	$(xml).find('track').each(function() {
		// Parse the XML
		var id = $(this).find('id').text();
		var title = $(this).find('name').text();
		var artist = $(this).find('artist').text();
		var source = $(this).find('source').text();
		var duration = $(this).find('duration').text();
		var uri = $(this).find('url').text();
		var mime = $(this).find('type').text();

		// No ID?
		if(!id)
			id = hex_md5(uri);

		// No MIME?
		if(!mime)
			mime = 'audio/ogg';

		// Local URL?
		if(type == 'local')
			uri = generateURL(uri);

		// Append the HTML code
		$(path_type).append('‣ ' + title + '');

		// Click event
		$(path_type + ' #' + id).click(function() {
			addMusic(id,  title, artist, source, duration, uri, mime, type);
		});
	});

	// The search is finished
	if(exists(content + ' .jamendo') && exists(content + ' .local')) {
		// Get the result values
		var jamendo = $(content + ' .jamendo').text();
		var local = $(content + ' .local').text();

		// Enable the input
		$(path + 'input').val('').removeAttr('disabled');

		// No result
		if(!jamendo && !local)
			$(path + '.no-results').show();

		// We must put a separator between the categories
		if(jamendo && local)
			$(content + ' .local').addClass('special');
	}
}

// Sends the music search requests
function searchMusic() {
	var path = '.music-content ';

	// We get the input string
	var string = $(path + 'input').val();

	// We lock the search input
	$(path + 'input').attr('disabled', true);

	// We reset the results
	$(path + '.list div').remove();
	$(path + '.no-results').hide();

	// Get the Jamendo results
	$.get('http://api.jamendo.com/get2/name+id+duration+url/track/xml/?searchquery=' + string + '&order=searchweight_desc', function(data) {
		parseMusic(data, 'jamendo');
	});

	// Get the local results
	$.get('./php/music-search.php?searchquery=' + string + '&location=' + JAPPIX_LOCATION, function(data) {
		parseMusic(data, 'local');
	});
}

// Performs an action on the music player
function actionMusic(action) {
	// Initialize
	var playThis = document.getElementById('top-content').getElementsByTagName('audio')[0];

	// Nothing to play, exit
	if(!playThis)
		return;

	var stopButton = $('#top-content a.stop');

	// User play a song
	if(action == 'play') {
		stopButton.show();
		playThis.load();
		playThis.play();
		playThis.addEventListener('ended', function() {
			actionMusic('stop');
		}, true);

		logThis('Music is now playing.');
	}

	// User stop the song or the song came to its end
	else if(action == 'stop') {
		stopButton.hide();
		playThis.pause();
		$('#top-content .music').removeClass('actived');
		$('.music-content .list a').removeClass('playing');
		publishMusic();

		logThis('Music is now stopped.');
	}
}

// Publishes the current title over PEP
function publishMusic(title, artist, source, duration, uri) {
	// We share the tune on PEP if enabled
	if(enabledPEP()) {
		/* REF: http://xmpp.org/extensions/xep-0118.html */

		var iq = new JSJaCIQ();
		iq.setType('set');

		// Create the main PubSub nodes
		var pubsub = iq.appendNode('pubsub', {'xmlns': NS_PUBSUB});
		var publish = pubsub.appendChild(iq.buildNode('publish', {'node': NS_TUNE, 'xmlns': NS_PUBSUB}));
		var item = publish.appendChild(iq.buildNode('item', {'xmlns': NS_PUBSUB}));
		var tune = item.appendChild(iq.buildNode('tune', {'xmlns': NS_TUNE}));

		// Enough data?
		if(title || artist || source || uri) {
			// Data array
			var nodes = new Array(
					'title',
					'artist',
					'source',
					'length',
					'uri'
				    );

			var values = new Array(
					title,
					artist,
					source,
					length,
					uri
				     );

			// Create the children nodes
			for(var i = 0; i < nodes.length; i++) {
				if(values[i])
					tune.appendChild(iq.buildNode(nodes[i], {'xmlns': NS_TUNE}, values[i]));
			}
		}

		con.send(iq);

		logThis('New tune sent: ' + title);
	}
}

// Adds a music title to the results
function addMusic(id, title, artist, source, duration, uri, mime, type) {
	var path = '.music-content ';

	// We create a new audio tag if it does not exist
	if(!exists('.music-audio'))
		$(path + '.player').prepend('<audio class="music-audio" />');

	// Apply the MIME type to the player
	$('.music-audio').attr('type', mime);

	// We apply the new source to the player
	if(type == 'jamendo')
		$('.music-audio').attr('src', 'http://api.jamendo.com/get2/stream/track/redirect/?id=' + id + '&streamencoding=ogg2');
	else
		$('.music-audio').attr('src', uri);

	// We play the target sound
	actionMusic('play');

	// We set the actived class
	$('#top-content .music').addClass('actived');

	// We set a current played track indicator
	$(path + '.list a').removeClass('playing');
	$(path + 'a#' + id).addClass('playing');

	// We publish what we listen
	publishMusic(title, artist, source, duration, uri);
}

// Plugin launcher
function launchMusic() {
	// When music search string submitted
	$('.music-content input').keyup(function(e) {
		// Enter : send
		if(e.keyCode == 13 && $(this).val())
			searchMusic();

		// Escape : quit
		if(e.keyCode == 27)
			closeBubbles();
	});
}
<commit_msg>Better music searcher.<commit_after>/*

Jappix - An open social platform
These are the music JS scripts for Jappix

-------------------------------------------------

License: AGPL
Author: Valérian Saliou
Contact: http://project.jappix.com/contact
Last revision: 07/11/10

*/

// Opens the music bubble
function openMusic() {
	var path = '.music-content';

	// Show the music bubble
	showBubble(path);
	$(path + ' input').focus();
}

// Parses the music search XML
function parseMusic(xml, type) {
	var path = '.music-content ';
	var content = path + '.list';
	var path_type = content + ' .' + type;

	// Create the result container
	if(!exists(path_type)) {
		var code = '

';

		if(type == 'local')
			$(content).prepend(code);
		else
			$(content).append(code);
	}

	// Fill the results
	$(xml).find('track').each(function() {
		// Parse the XML
		var id = $(this).find('id').text();
		var title = $(this).find('name').text();
		var artist = $(this).find('artist').text();
		var source = $(this).find('source').text();
		var duration = $(this).find('duration').text();
		var uri = $(this).find('url').text();
		var mime = $(this).find('type').text();

		// No ID?
		if(!id)
			id = hex_md5(uri);

		// No MIME?
		if(!mime)
			mime = 'audio/ogg';

		// Local URL?
		if(type == 'local')
			uri = generateURL(uri);

		// Append the HTML code
		$(path_type).append('' + title + '');

		// Current playing song?
		var current_song = $(path_type + ' a[data-id=' + id + ']');

		if(exists('.music-audio[data-id=' + id + ']'))
			current_song.addClass('playing');

		// Click event
		current_song.click(function() {
			addMusic(id,  title, artist, source, duration, uri, mime, type);
		});
	});

	// The search is finished
	if(exists(content + ' .jamendo') && exists(content + ' .local')) {
		// Get the result values
		var jamendo = $(content + ' .jamendo').text();
		var local = $(content + ' .local').text();

		// Enable the input
		$(path + 'input').val('').removeAttr('disabled');

		// No result
		if(!jamendo && !local)
			$(path + '.no-results').show();

		// We must put a separator between the categories
		if(jamendo && local)
			$(content + ' .local').addClass('special');
	}
}

// Sends the music search requests
function searchMusic() {
	var path = '.music-content ';

	// We get the input string
	var string = $(path + 'input').val();

	// We lock the search input
	$(path + 'input').attr('disabled', true);

	// We reset the results
	$(path + '.list div').remove();
	$(path + '.no-results').hide();

	// Get the Jamendo results
	$.get('http://api.jamendo.com/get2/name+id+duration+url/track/xml/?searchquery=' + string + '&order=searchweight_desc', function(data) {
		parseMusic(data, 'jamendo');
	});

	// Get the local results
	$.get('./php/music-search.php?searchquery=' + string + '&location=' + JAPPIX_LOCATION, function(data) {
		parseMusic(data, 'local');
	});
}

// Performs an action on the music player
function actionMusic(action) {
	// Initialize
	var playThis = document.getElementById('top-content').getElementsByTagName('audio')[0];

	// Nothing to play, exit
	if(!playThis)
		return;

	var stopButton = $('#top-content a.stop');

	// User play a song
	if(action == 'play') {
		stopButton.show();
		playThis.load();
		playThis.play();
		playThis.addEventListener('ended', function() {
			actionMusic('stop');
		}, true);

		logThis('Music is now playing.');
	}

	// User stop the song or the song came to its end
	else if(action == 'stop') {
		stopButton.hide();
		playThis.pause();
		$('#top-content .music').removeClass('actived');
		$('.music-content .list a').removeClass('playing');
		$('.music-audio').remove();
		publishMusic();

		logThis('Music is now stopped.');
	}
}

// Publishes the current title over PEP
function publishMusic(title, artist, source, duration, uri) {
	// We share the tune on PEP if enabled
	if(enabledPEP()) {
		/* REF: http://xmpp.org/extensions/xep-0118.html */

		var iq = new JSJaCIQ();
		iq.setType('set');

		// Create the main PubSub nodes
		var pubsub = iq.appendNode('pubsub', {'xmlns': NS_PUBSUB});
		var publish = pubsub.appendChild(iq.buildNode('publish', {'node': NS_TUNE, 'xmlns': NS_PUBSUB}));
		var item = publish.appendChild(iq.buildNode('item', {'xmlns': NS_PUBSUB}));
		var tune = item.appendChild(iq.buildNode('tune', {'xmlns': NS_TUNE}));

		// Enough data?
		if(title || artist || source || uri) {
			// Data array
			var nodes = new Array(
					'title',
					'artist',
					'source',
					'length',
					'uri'
				    );

			var values = new Array(
					title,
					artist,
					source,
					length,
					uri
				     );

			// Create the children nodes
			for(var i = 0; i < nodes.length; i++) {
				if(values[i])
					tune.appendChild(iq.buildNode(nodes[i], {'xmlns': NS_TUNE}, values[i]));
			}
		}

		con.send(iq);

		logThis('New tune sent: ' + title);
	}
}

// Adds a music title to the results
function addMusic(id, title, artist, source, duration, uri, mime, type) {
	var path = '.music-content ';

	// We remove & create a new audio tag
	$('.music-audio').remove();
	$(path + '.player').prepend('<audio class="music-audio" type="' + mime + '" data-id="' + id + '" />');

	// We apply the new source to the player
	if(type == 'jamendo')
		$('.music-audio').attr('src', 'http://api.jamendo.com/get2/stream/track/redirect/?id=' + id + '&streamencoding=ogg2');
	else
		$('.music-audio').attr('src', uri);

	// We play the target sound
	actionMusic('play');

	// We set the actived class
	$('#top-content .music').addClass('actived');

	// We set a current played track indicator
	$(path + '.list a').removeClass('playing');
	$(path + 'a[data-id=' + id + ']').addClass('playing');

	// We publish what we listen
	publishMusic(title, artist, source, duration, uri);
}

// Plugin launcher
function launchMusic() {
	// When music search string submitted
	$('.music-content input').keyup(function(e) {
		// Enter : send
		if(e.keyCode == 13 && $(this).val())
			searchMusic();

		// Escape : quit
		if(e.keyCode == 27)
			closeBubbles();
	});
}
<|endoftext|>


<commit_before>
define([
    'flight/lib/component',
    'tpl!./overlay',
    'util/formatters',
    'service/workspace',
    'service/ontology',
    'data'
], function(defineComponent, template, F, WorkspaceService, OntologyService, appData) {
    'use strict';

    var LAST_SAVED_UPDATE_FREQUENCY_SECONDS = 30,
        MENUBAR_WIDTH = 30,
        UPDATE_WORKSPACE_DIFF_SECONDS = 3,
        SHOW_UNPUBLUSHED_CHANGES_SECONDS = 3;

    return defineComponent(WorkspaceOverlay);

    function isWorkspaceDiffPost(settings) {
        var route = ~['workspace/undo', 'workspace/publish'].indexOf(settings.url),
            isPost = (/post/i).test(settings.type);

        return !!(route && isPost);
    }

    function WorkspaceOverlay() {

        var workspaceService = new WorkspaceService(),
            ontologyService = new OntologyService();

        this.defaultAttrs({
            userSelector: '.user',
            nameSelector: '.name',
            subtitleSelector: '.subtitle'
        });

        this.after('initialize', function() {
            var self = this;

            requestAnimationFrame(function() {
                MENUBAR_WIDTH = $('.menubar-pane').width();
            })

            this.userDeferred = $.Deferred();
            if (window.currentUser) {
                this.userDeferred.resolve();
            }

            this.workspaceDeferred = $.Deferred();
            this.updateDiffBadge = _.throttle(this.updateDiffBadge.bind(this), UPDATE_WORKSPACE_DIFF_SECONDS * 1000)

            $.when(this.userDeferred, this.workspaceDeferred).done(function() {
                self.$node.show();

                self.updateUserTooltip({user: window.currentUser});

                requestAnimationFrame(function() {
                    self.$node.addClass('visible');
                });
            })

            this.$node.hide().html(template({}));

            this.on(document, 'workspaceSaving', this.onWorkspaceSaving);
            this.on(document, 'workspaceSaved', this.onWorkspaceSaved);
            this.on(document, 'workspaceLoaded', this.onWorkspaceLoaded);
            this.on(document, 'switchWorkspace', this.onSwitchWorkspace);
            this.on(document, 'graphPaddingUpdated', this.onGraphPaddingUpdated);
            this.on(document, 'currentUserChanged', this.onCurrentUserChanged);
            this.on(document, 'relationshipsLoaded', this.onRelationshipsLoaded);

            this.on(document, 'verticesUpdated', this.updateDiffBadge);
            this.on(document, 'verticesAdded', this.updateDiffBadge);
            this.on(document, 'edgesDeleted', this.updateDiffBadge);
            this.on(document, 'updateDiff', this.updateDiffBadge);

            this.on(document, 'showDiffPanel', this.showDiffPanel);
            this.on(document, 'escape', this.closeDiffPanel);

            this.trigger(document, 'registerKeyboardShortcuts', {
                scope: ['graph.help.scope', 'map.help.scope'].map(i18n),
                shortcuts: {
                    'alt-d':  { fire: 'showDiffPanel', desc: i18n('workspaces.help.show_diff') }
                }
            });
        });

        this.showDiffPanel = function() {
            var badge = this.$node.find('.badge');
            if (badge.is(':visible')) {
                badge.popover('show');
            }
        };

        this.closeDiffPanel = function() {
            var badge = this.$node.find('.badge');
            if (badge.is(':visible')) {
                badge.popover('hide');
            }
        };

        this.onCurrentUserChanged = function(event, data) {
            this.userDeferred.resolve();
            this.updateUserTooltip(data);
        }

        this.onGraphPaddingUpdated = function(event, data) {
            this.$node.css('left', data.padding.l + MENUBAR_WIDTH);

            var width = $(window).width(),
                height = $(window).height(),
                popover = this.$node.find('.popover'),
                paddingH = 100,
                paddingV = 75,
                popoverCss = {
                    maxWidth: (width - MENUBAR_WIDTH - (data.padding.l + data.padding.r) - paddingH),
                    maxHeight: (height - (data.padding.t + data.padding.b) - paddingV)
                };

            this.popoverCss = popoverCss;
            if (popover.length) {
                this.updatePopoverSize(popover);
            }
        };

        this.updatePopoverSize = function(tip) {
            var css = {};
            if (tip.width() > this.popoverCss.maxWidth) {
                css.width = this.popoverCss.maxWidth + 'px';
            }
            if (tip.height() > this.popoverCss.maxHeight) {
                css.height = this.popoverCss.maxHeight + 'px';
            }

            tip.resizable('option', 'maxWidth', this.popoverCss.maxWidth);
            tip.resizable('option', 'maxHeight', this.popoverCss.maxHeight);
            if (_.keys(css).length) {
                tip.css(css);
            }
        }

        this.setContent = function(title, editable, subtitle) {
            this.select('nameSelector').text(title);
            this.select('subtitleSelector').html(
                editable === false ?
                    i18n('workspaces.overlay.read_only') :
                    subtitle
            );
        };

        this.onSwitchWorkspace = function() {
            this.previousDiff = null;
            this.$node.find('.badge').popover('destroy').remove();
        };

        this.onWorkspaceLoaded = function(event, data) {
            this.workspaceDeferred.resolve();
            this.setContent(data.title, data.editable, i18n('workspaces.overlay.no_changes'));
            clearTimeout(this.updateTimer);
            this.updateWorkspaceTooltip(data);
            this.updateDiffBadge();
        };

        this.onRelationshipsLoaded = function(event, data) {
            this.updateWorkspaceTooltip(data);
        };

        this.onWorkspaceSaving = function(event, data) {
            this.select('subtitleSelector').text(i18n('workspaces.overlay.saving'));
            clearTimeout(this.updateTimer);
            this.updateWorkspaceTooltip(data);
        };

        this.onWorkspaceSaved = function(event, data) {
            clearTimeout(this.updateTimer);
            this.lastSaved = F.date.utc(Date.now());

            if (data.title) {
                this.select('nameSelector').text(data.title);
            }

            this.updateWorkspaceTooltip(data);

            var subtitle = this.select('subtitleSelector').text(
                    i18n('workspaces.overlay.last_saved_moments_ago')
                ),
                setTimer = function() {
                    this.updateTimer = setTimeout(function() {

                        var time = F.date.relativeToNow(this.lastSaved);
                        subtitle.text(
                            i18n('workspaces.overlay.last_saved_time', time)
                        );

                        setTimer();
                    }.bind(this), LAST_SAVED_UPDATE_FREQUENCY_SECONDS * 1000);
                }.bind(this);

            setTimer();
        };

        this.onDiffBadgeMouse = function(event) {
            this.trigger(
                event.type === 'mouseenter' ?  'focusVertices' : 'defocusVertices',
                { vertexIds: this.currentDiffIds || [] }
            );
        };

        this.updateDiffBadge = function(event, data) {
            var self = this,
                node = this.select('nameSelector'),
                badge = this.$node.find('.badge');

            if (event && event.type === 'verticesUpdated') {
                if (!data || !data.options || data.options.originalEvent !== 'propertiesChange') {
                    return;
                }
            }

            if (!badge.length) {
                badge = $('')
                    .insertAfter(node)
                    .on('mouseenter mouseleave', this.onDiffBadgeMouse.bind(this))
            }

            $.when(
                workspaceService.diff(appData.workspaceId),
                ontologyService.properties())
                .fail(function() {
                    badge.removePrefixedClasses('badge-').addClass('badge-important')
                        .popover('destroy')
                        .attr('title', i18n('workspaces.overlay.error'))
                        .text('!');
                })
                .done(function(response, ontologyProperties) {
                    var diffs = response[0].diffs,
                        diffsWithoutVisibleProperty = _.map(diffs, function(d) {
                            return _.omit(d, 'visible');
                        });

                    // Check if same
                    if (self.previousDiff && _.isEqual(diffsWithoutVisibleProperty, self.previousDiff)) {
                        return;
                    }
                    self.previousDiff = diffsWithoutVisibleProperty;

                    var vertexDiffsById = _.indexBy(diffs, function(diff) {
                            return diff.vertexId;
                        }),
                        countOfTitleChanges = 0,
                        filteredDiffs = _.filter(diffs, function(diff) {
                            if (diff.type !== 'PropertyDiffItem') return true;

                            var ontologyProperty = ontologyProperties.byTitle[diff.name];
                            if (!ontologyProperty || !ontologyProperty.userVisible) return false;
                            if (diff.name === 'title' && vertexDiffsById[diff.elementId]) {
                                countOfTitleChanges++;
                            }
                            return true;
                        }),
                        count = filteredDiffs.length - countOfTitleChanges,
                        formattedCount = F.number.pretty(count);

                    self.currentDiffIds = _.uniq(filteredDiffs.map(function(diff) {
                        return diff.vertexId || diff.elementId || diff.edgeId;
                    }));

                    require(['workspaces/diff/diff'], function(Diff) {
                        var popover = badge.data('popover'),
                            tip = popover && popover.tip();

                        if (tip && tip.is(':visible')) {
                            self.trigger(popover.tip().find('.popover-content'),
                                 'diffsChanged',
                                 { diffs: filteredDiffs });
                            popover.show();
                        } else {
                            badge
                                .popover('destroy')
                                .popover({
                                    placement: 'top',
                                    content: i18n('workspaces.diff.loading'),
                                    title: i18n('workspaces.diff.header.unpublished_changes')
                                });

                            popover = badge.data('popover');
                            tip = popover.tip();

                            var left = 10;
                            tip.css({
                                    width: '400px',
                                    height: '250px'
                                })
                                .data('sizePreference', 'diff')
                                .find('.arrow').css({
                                    left: parseInt(badge.position().left - (left / 2) + 1, 10) + 'px',
                                    marginLeft: 0
                                })

                            // We fill in our own content
                            popover.setContent = function() {}
                            badge.on('shown', function() {
                                var css = {
                                    top: (parseInt(tip.css('top')) - 10) + 'px'
                                };
                                tip.resizable({
                                    handles: 'n, e, ne',
                                    maxWidth: self.popoverCss.maxWidth,
                                    maxHeight: self.popoverCss.maxHeight
                                }).css({top: top});

                                self.updatePopoverSize(tip);
                            })

                            Diff.teardownAll();
                            Diff.attachTo(tip.find('.popover-content'), {
                                diffs: filteredDiffs
                            });
                        }
                    });

                    badge.removePrefixedClasses('badge-').addClass('badge-info')
                        .attr('title', i18n('workspaces.diff.unpublished_change.' + (
                            formattedCount === 1 ?
                            'one' : 'some'), formattedCount))
                        .text(count > 0 ? formattedCount : '');

                    if (count > 0) {
                        self.animateBadge(badge, formattedCount);
                    } else if (count === 0) {
                        badge.popover('destroy');
                    }
                })
        };

        var badgeReset, animateTimer;
        this.animateBadge = function(badge, formattedCount) {
            badge.text(formattedCount).css('width', 'auto');

            var previousWidth = badge.width(),
                html = '' + formattedCount + '' +
                    ' ' + i18n('workspaces.diff.unpublished') + '',
                findWidth = function() {
                    return (
                        badge.find('.number').outerWidth(true) +
                        badge.find('.suffix').outerWidth(true) +
                        parseInt(badge.css('paddingRight'), 10) * 2
                    ) + 'px';
                };

            if (animateTimer) {
                clearTimeout(animateTimer);
                animateTimer = _.delay(
                    badgeReset.bind(null, previousWidth, formattedCount),
                    SHOW_UNPUBLUSHED_CHANGES_SECONDS * 1000
                );
                return badge.html(html).css({ width: findWidth() })
            }

            var duration = '0.5s';
            badge.css({
                width: previousWidth + 'px',
                backgroundColor: '#0088cc',
                transition: 'all cubic-bezier(.29,.79,0,1.48) ' + duration,
                position: 'relative',
            }).html(html);

            requestAnimationFrame(function() {
                badge.css({
                    backgroundColor: '#1ab2ff',
                    width: findWidth()
                }).find('.suffix').css({
                    transition: 'opacity ease-out ' + duration,
                })

                animateTimer = _.delay((badgeReset = function(previousWidth, formattedCount) {
                    animateTimer = null;
                    badge.on(TRANSITION_END, function(e) {
                        if (e.originalEvent.propertyName === 'width') {
                            badge.off(TRANSITION_END);
                            badge.text(formattedCount).css('width', 'auto');
                        }
                    }).css({
                        transition: 'all cubic-bezier(.92,-0.42,.37,1.31) ' + duration,
                        backgroundColor: '#0088cc',
                        width: previousWidth + 'px'
                    }).find('.suffix').css('opacity',0);
                }).bind(null, previousWidth, formattedCount), SHOW_UNPUBLUSHED_CHANGES_SECONDS * 1000);
            })
        };

        this.updateUserTooltip = function(data) {
            if (data && data.user) {
                this.select('userSelector').text(data.user.displayName)
                    .tooltip('destroy')
                    .tooltip({
                        placement: 'right',
                        html: true,
                        title: '' +
                            i18n('workspaces.overlay.authorizations') +
                            ' ' +
                                    (data.user.authorizations.join(', ') ||
                                     i18n('workspaces.overlay.authorizations.none')) +
                                '' +
                                '
' +
                                i18n('workspaces.overlay.privileges') +
                                ' ' +
                                    _.without(data.user.privileges, 'READ').join(', ') +
                                '
',
                        trigger: 'hover',
                        delay: { show: 500, hide: 0 }
                    })
            }
        }

        this.updateWorkspaceTooltip = function(data) {
            if (data && data.data && data.data.vertices) {
                this.verticesCount = data.data.vertices.length;
            }
            if (this.verticesCount === 0) {
                this.edgesCount = 0;
            } else if (data.relationships) {
                this.edgesCount = data.relationships.length;
            } else {
                this.edgesCount = $('.cytoscape-container').cytoscape('get').edges().length;
            }

            var name = this.select('nameSelector'),
                tooltip = name.data('tooltip'),
                tip = tooltip && tooltip.tip(),
                text = i18n('workspaces.overlay.vertices') + ': ' +
                    F.number.pretty(this.verticesCount || 0) + ', ' +
                    i18n('workspaces.overlay.edges') + ': ' +
                    F.number.pretty(this.edgesCount || 0)

            if (tip && tip.is(':visible')) {
                tip.find('.tooltip-inner span').text(text);
            } else {
                name
                    .tooltip('destroy')
                    .tooltip({
                        placement: 'right',
                        html: true,
                        title: '' + text + '',
                        trigger: 'hover',
                        delay: { show: 500, hide: 0 }
                    });
            }

        }
    }
});
<commit_msg>Fix diff panel badge animation jumping at end<commit_after>
define([
    'flight/lib/component',
    'tpl!./overlay',
    'util/formatters',
    'service/workspace',
    'service/ontology',
    'data'
], function(defineComponent, template, F, WorkspaceService, OntologyService, appData) {
    'use strict';

    var LAST_SAVED_UPDATE_FREQUENCY_SECONDS = 30,
        MENUBAR_WIDTH = 30,
        UPDATE_WORKSPACE_DIFF_SECONDS = 3,
        SHOW_UNPUBLUSHED_CHANGES_SECONDS = 3;

    return defineComponent(WorkspaceOverlay);

    function isWorkspaceDiffPost(settings) {
        var route = ~['workspace/undo', 'workspace/publish'].indexOf(settings.url),
            isPost = (/post/i).test(settings.type);

        return !!(route && isPost);
    }

    function WorkspaceOverlay() {

        var workspaceService = new WorkspaceService(),
            ontologyService = new OntologyService();

        this.defaultAttrs({
            userSelector: '.user',
            nameSelector: '.name',
            subtitleSelector: '.subtitle'
        });

        this.after('initialize', function() {
            var self = this;

            requestAnimationFrame(function() {
                MENUBAR_WIDTH = $('.menubar-pane').width();
            })

            this.userDeferred = $.Deferred();
            if (window.currentUser) {
                this.userDeferred.resolve();
            }

            this.workspaceDeferred = $.Deferred();
            this.updateDiffBadge = _.throttle(this.updateDiffBadge.bind(this), UPDATE_WORKSPACE_DIFF_SECONDS * 1000)

            $.when(this.userDeferred, this.workspaceDeferred).done(function() {
                self.$node.show();

                self.updateUserTooltip({user: window.currentUser});

                requestAnimationFrame(function() {
                    self.$node.addClass('visible');
                });
            })

            this.$node.hide().html(template({}));

            this.on(document, 'workspaceSaving', this.onWorkspaceSaving);
            this.on(document, 'workspaceSaved', this.onWorkspaceSaved);
            this.on(document, 'workspaceLoaded', this.onWorkspaceLoaded);
            this.on(document, 'switchWorkspace', this.onSwitchWorkspace);
            this.on(document, 'graphPaddingUpdated', this.onGraphPaddingUpdated);
            this.on(document, 'currentUserChanged', this.onCurrentUserChanged);
            this.on(document, 'relationshipsLoaded', this.onRelationshipsLoaded);

            this.on(document, 'verticesUpdated', this.updateDiffBadge);
            this.on(document, 'verticesAdded', this.updateDiffBadge);
            this.on(document, 'edgesDeleted', this.updateDiffBadge);
            this.on(document, 'updateDiff', this.updateDiffBadge);

            this.on(document, 'showDiffPanel', this.showDiffPanel);
            this.on(document, 'escape', this.closeDiffPanel);

            this.trigger(document, 'registerKeyboardShortcuts', {
                scope: ['graph.help.scope', 'map.help.scope'].map(i18n),
                shortcuts: {
                    'alt-d':  { fire: 'showDiffPanel', desc: i18n('workspaces.help.show_diff') }
                }
            });
        });

        this.showDiffPanel = function() {
            var badge = this.$node.find('.badge');
            if (badge.is(':visible')) {
                badge.popover('show');
            }
        };

        this.closeDiffPanel = function() {
            var badge = this.$node.find('.badge');
            if (badge.is(':visible')) {
                badge.popover('hide');
            }
        };

        this.onCurrentUserChanged = function(event, data) {
            this.userDeferred.resolve();
            this.updateUserTooltip(data);
        }

        this.onGraphPaddingUpdated = function(event, data) {
            this.$node.css('left', data.padding.l + MENUBAR_WIDTH);

            var width = $(window).width(),
                height = $(window).height(),
                popover = this.$node.find('.popover'),
                paddingH = 100,
                paddingV = 75,
                popoverCss = {
                    maxWidth: (width - MENUBAR_WIDTH - (data.padding.l + data.padding.r) - paddingH),
                    maxHeight: (height - (data.padding.t + data.padding.b) - paddingV)
                };

            this.popoverCss = popoverCss;
            if (popover.length) {
                this.updatePopoverSize(popover);
            }
        };

        this.updatePopoverSize = function(tip) {
            var css = {};
            if (tip.width() > this.popoverCss.maxWidth) {
                css.width = this.popoverCss.maxWidth + 'px';
            }
            if (tip.height() > this.popoverCss.maxHeight) {
                css.height = this.popoverCss.maxHeight + 'px';
            }

            tip.resizable('option', 'maxWidth', this.popoverCss.maxWidth);
            tip.resizable('option', 'maxHeight', this.popoverCss.maxHeight);
            if (_.keys(css).length) {
                tip.css(css);
            }
        }

        this.setContent = function(title, editable, subtitle) {
            this.select('nameSelector').text(title);
            this.select('subtitleSelector').html(
                editable === false ?
                    i18n('workspaces.overlay.read_only') :
                    subtitle
            );
        };

        this.onSwitchWorkspace = function() {
            this.previousDiff = null;
            this.$node.find('.badge').popover('destroy').remove();
        };

        this.onWorkspaceLoaded = function(event, data) {
            this.workspaceDeferred.resolve();
            this.setContent(data.title, data.editable, i18n('workspaces.overlay.no_changes'));
            clearTimeout(this.updateTimer);
            this.updateWorkspaceTooltip(data);
            this.updateDiffBadge();
        };

        this.onRelationshipsLoaded = function(event, data) {
            this.updateWorkspaceTooltip(data);
        };

        this.onWorkspaceSaving = function(event, data) {
            this.select('subtitleSelector').text(i18n('workspaces.overlay.saving'));
            clearTimeout(this.updateTimer);
            this.updateWorkspaceTooltip(data);
        };

        this.onWorkspaceSaved = function(event, data) {
            clearTimeout(this.updateTimer);
            this.lastSaved = F.date.utc(Date.now());

            if (data.title) {
                this.select('nameSelector').text(data.title);
            }

            this.updateWorkspaceTooltip(data);

            var subtitle = this.select('subtitleSelector').text(
                    i18n('workspaces.overlay.last_saved_moments_ago')
                ),
                setTimer = function() {
                    this.updateTimer = setTimeout(function() {

                        var time = F.date.relativeToNow(this.lastSaved);
                        subtitle.text(
                            i18n('workspaces.overlay.last_saved_time', time)
                        );

                        setTimer();
                    }.bind(this), LAST_SAVED_UPDATE_FREQUENCY_SECONDS * 1000);
                }.bind(this);

            setTimer();
        };

        this.onDiffBadgeMouse = function(event) {
            this.trigger(
                event.type === 'mouseenter' ?  'focusVertices' : 'defocusVertices',
                { vertexIds: this.currentDiffIds || [] }
            );
        };

        this.updateDiffBadge = function(event, data) {
            var self = this,
                node = this.select('nameSelector'),
                badge = this.$node.find('.badge');

            if (event && event.type === 'verticesUpdated') {
                if (!data || !data.options || data.options.originalEvent !== 'propertiesChange') {
                    return;
                }
            }

            if (!badge.length) {
                badge = $('')
                    .insertAfter(node)
                    .on('mouseenter mouseleave', this.onDiffBadgeMouse.bind(this))
            }

            $.when(
                workspaceService.diff(appData.workspaceId),
                ontologyService.properties())
                .fail(function() {
                    badge.removePrefixedClasses('badge-').addClass('badge-important')
                        .popover('destroy')
                        .attr('title', i18n('workspaces.overlay.error'))
                        .text('!');
                })
                .done(function(response, ontologyProperties) {
                    var diffs = response[0].diffs,
                        diffsWithoutVisibleProperty = _.map(diffs, function(d) {
                            return _.omit(d, 'visible');
                        });

                    // Check if same
                    if (self.previousDiff && _.isEqual(diffsWithoutVisibleProperty, self.previousDiff)) {
                        return;
                    }
                    self.previousDiff = diffsWithoutVisibleProperty;

                    var vertexDiffsById = _.indexBy(diffs, function(diff) {
                            return diff.vertexId;
                        }),
                        countOfTitleChanges = 0,
                        filteredDiffs = _.filter(diffs, function(diff) {
                            if (diff.type !== 'PropertyDiffItem') return true;

                            var ontologyProperty = ontologyProperties.byTitle[diff.name];
                            if (!ontologyProperty || !ontologyProperty.userVisible) return false;
                            if (diff.name === 'title' && vertexDiffsById[diff.elementId]) {
                                countOfTitleChanges++;
                            }
                            return true;
                        }),
                        count = filteredDiffs.length - countOfTitleChanges,
                        formattedCount = F.number.pretty(count);

                    self.currentDiffIds = _.uniq(filteredDiffs.map(function(diff) {
                        return diff.vertexId || diff.elementId || diff.edgeId;
                    }));

                    require(['workspaces/diff/diff'], function(Diff) {
                        var popover = badge.data('popover'),
                            tip = popover && popover.tip();

                        if (tip && tip.is(':visible')) {
                            self.trigger(popover.tip().find('.popover-content'),
                                 'diffsChanged',
                                 { diffs: filteredDiffs });
                            popover.show();
                        } else {
                            badge
                                .popover('destroy')
                                .popover({
                                    placement: 'top',
                                    content: i18n('workspaces.diff.loading'),
                                    title: i18n('workspaces.diff.header.unpublished_changes')
                                });

                            popover = badge.data('popover');
                            tip = popover.tip();

                            var left = 10;
                            tip.css({
                                    width: '400px',
                                    height: '250px'
                                })
                                .data('sizePreference', 'diff')
                                .find('.arrow').css({
                                    left: parseInt(badge.position().left - (left / 2) + 1, 10) + 'px',
                                    marginLeft: 0
                                })

                            // We fill in our own content
                            popover.setContent = function() {}
                            badge.on('shown', function() {
                                var css = {
                                    top: (parseInt(tip.css('top')) - 10) + 'px'
                                };
                                tip.resizable({
                                    handles: 'n, e, ne',
                                    maxWidth: self.popoverCss.maxWidth,
                                    maxHeight: self.popoverCss.maxHeight
                                }).css({top: top});

                                self.updatePopoverSize(tip);
                            })

                            Diff.teardownAll();
                            Diff.attachTo(tip.find('.popover-content'), {
                                diffs: filteredDiffs
                            });
                        }
                    });

                    badge.removePrefixedClasses('badge-').addClass('badge-info')
                        .attr('title', i18n('workspaces.diff.unpublished_change.' + (
                            formattedCount === 1 ?
                            'one' : 'some'), formattedCount))
                        .text(count > 0 ? formattedCount : '');

                    if (count > 0) {
                        self.animateBadge(badge, formattedCount);
                    } else if (count === 0) {
                        badge.popover('destroy');
                    }
                })
        };

        var badgeReset, animateTimer;
        this.animateBadge = function(badge, formattedCount) {
            badge.text(formattedCount).css('width', 'auto');

            var html = '' + formattedCount + '' +
                    ' ' + i18n('workspaces.diff.unpublished') + '',
                previousWidth = badge.outerWidth(),
                findWidth = function() {
                    return (
                        badge.find('.number').outerWidth(true) +
                        badge.find('.suffix').outerWidth(true) +
                        parseInt(badge.css('paddingRight'), 10) * 2
                    ) + 'px';
                };

            if (animateTimer) {
                clearTimeout(animateTimer);
                animateTimer = _.delay(
                    badgeReset.bind(null, previousWidth, formattedCount),
                    SHOW_UNPUBLUSHED_CHANGES_SECONDS * 1000
                );
                return badge.html(html).css({ width: findWidth() })
            }

            var duration = '0.5s';
            badge.css({
                width: previousWidth + 'px',
                backgroundColor: '#0088cc',
                transition: 'all cubic-bezier(.29,.79,0,1.48) ' + duration,
                position: 'relative',
            }).html(html);

            requestAnimationFrame(function() {
                badge.css({
                    backgroundColor: '#1ab2ff',
                    width: findWidth()
                }).find('.suffix').css({
                    transition: 'opacity ease-out ' + duration,
                })

                animateTimer = _.delay((badgeReset = function(previousWidth, formattedCount) {
                    animateTimer = null;
                    badge.on(TRANSITION_END, function(e) {
                        if (e.originalEvent.propertyName === 'width') {
                            badge.off(TRANSITION_END);
                            badge.text(formattedCount).css('width', 'auto');
                        }
                    }).css({
                        transition: 'all cubic-bezier(.92,-0.42,.37,1.31) ' + duration,
                        backgroundColor: '#0088cc',
                        width: previousWidth + 'px'
                    }).find('.suffix').css('opacity',0);
                }).bind(null, previousWidth, formattedCount), SHOW_UNPUBLUSHED_CHANGES_SECONDS * 1000);
            })
        };

        this.updateUserTooltip = function(data) {
            if (data && data.user) {
                this.select('userSelector').text(data.user.displayName)
                    .tooltip('destroy')
                    .tooltip({
                        placement: 'right',
                        html: true,
                        title: '' +
                            i18n('workspaces.overlay.authorizations') +
                            ' ' +
                                    (data.user.authorizations.join(', ') ||
                                     i18n('workspaces.overlay.authorizations.none')) +
                                '' +
                                '
' +
                                i18n('workspaces.overlay.privileges') +
                                ' ' +
                                    _.without(data.user.privileges, 'READ').join(', ') +
                                '
',
                        trigger: 'hover',
                        delay: { show: 500, hide: 0 }
                    })
            }
        }

        this.updateWorkspaceTooltip = function(data) {
            if (data && data.data && data.data.vertices) {
                this.verticesCount = data.data.vertices.length;
            }
            if (this.verticesCount === 0) {
                this.edgesCount = 0;
            } else if (data.relationships) {
                this.edgesCount = data.relationships.length;
            } else {
                this.edgesCount = $('.cytoscape-container').cytoscape('get').edges().length;
            }

            var name = this.select('nameSelector'),
                tooltip = name.data('tooltip'),
                tip = tooltip && tooltip.tip(),
                text = i18n('workspaces.overlay.vertices') + ': ' +
                    F.number.pretty(this.verticesCount || 0) + ', ' +
                    i18n('workspaces.overlay.edges') + ': ' +
                    F.number.pretty(this.edgesCount || 0)

            if (tip && tip.is(':visible')) {
                tip.find('.tooltip-inner span').text(text);
            } else {
                name
                    .tooltip('destroy')
                    .tooltip({
                        placement: 'right',
                        html: true,
                        title: '' + text + '',
                        trigger: 'hover',
                        delay: { show: 500, hide: 0 }
                    });
            }

        }
    }
});
<|endoftext|>


<commit_before>"use strict";
const RACE_JSON_URL = "data/races.json";

let amount, count;

let raceData;

function loadRaceJson () {
	loadJSON(RACE_JSON_URL, onJsonLoad)
}

window.onload = function load () {
	loadRaceJson();
	prevent();
};

function onJsonLoad (data) {
	raceData = data.race;

	$("#rollbutton").click(rollstats);
	$(".base").on("input", changeBase);
	$("input.choose").on("change", choose);

	const names = raceData.map(x => x.name).sort();
	const options = names.map(name => `<option>${name}</option>`).join();
	$("#race").append(options).change(changeRace).change();

	if (window.location.hash) window.onhashchange();
	else window.location.hash = "#rolled";
}

const STATS_MIN = 8;
const STATS_MAX = 15;

function prevent () {
	for (let i = 1; i < 7; ++i) {
		const input = $(`#inputBox${i}`);
		input.on("change", function (e) {
			let num = parseInt(this.value);
			if (isNaN(num)) {
				this.value = 8;
			} else {
				this.value = Math.max(Math.min(num, STATS_MAX), STATS_MIN);
			}
			changeTotal();
		})
	}
}

window.onhashchange = function hashchange () {
	const hash = window.location.hash.slice(1);
	$(".statmethod").hide();
	$("#" + hash).show();
};

function getCost (n) {
	if (n < 14) return n - 8;
	if (n === 14) return 7;
	return 9
}

function choose () {
	if ($("input.choose:checked").length > count) return this.checked = false;

	$(".racial", this.parentNode.parentNode)
		.val(this.checked ? amount : 0);
	changeTotal()
}

function changeRace () {
	const race = this.value;
	const stats = raceData
		.find(({name}) => name === race).ability;

	$(".racial").val(0);
	for (const key in stats) $(`#${key} .racial`).val(stats[key])

	changeTotal();
	$(".choose").hide().prop("checked", false);

	if (!stats.choose) return;

	const {from} = stats.choose[0];
	amount = stats.choose[0].amount || 1;
	count = stats.choose[0].count;

	$("td.choose").text(`Choose ${count}`).show();
	from.forEach(key => $(`#${key} .choose`).show())
}

function changeTotal () {
	$("#pointbuy tr[id]").each((i, el) => {
		const [base, racial, total, mod] = $("input", el).get();
		const raw = total.value = Number(base.value) + Number(racial.value);
		mod.value = Math.floor((raw - 10) / 2)
	})
}

function changeBase (e) {
	const budget = Number($("#budget").val());

	let cost = 0;
	$(".base").each((i, el) => cost += getCost(Number(el.value)));

	if (cost > budget) return this.value = this.dataset.prev;

	this.dataset.prev = this.value;
	$("#remaining").val(budget - cost);

	changeTotal()
}

function rollstats () {
	var rolls = [];
	for (var i = 0; i < 6; i++) {
		var curroll = droll.roll("4d6").rolls.sort().splice(1);
		curroll = curroll[0] + curroll[1] + curroll[2];
		rolls.push(curroll);
	}

	$("#rolled #rolls").prepend("
" + rolls.join(", ") + "
");
	$("#rolled #rolls p:eq(10)").remove();
}
<commit_msg>refactor(statgen.js): streamlined code<commit_after>"use strict";
const RACE_JSON_URL = "data/races.json";

let amount, count;

let raceData;

function loadRaceJson () {
	loadJSON(RACE_JSON_URL, onJsonLoad)
}

window.onload = function load () {
	loadRaceJson();
	prevent();
};

function onJsonLoad (data) {
	raceData = data.race;

	$("#rollbutton").click(rollstats);
	$(".base").on("input", changeBase);
	$("input.choose").on("change", choose);

	const names = raceData.map(x => x.name).sort();
	const options = names.map(name => `<option>${name}</option>`).join();
	$("#race").append(options).change(changeRace).change();

	if (window.location.hash) window.onhashchange();
	else window.location.hash = "#rolled";
}

const STATS_MIN = 8;
const STATS_MAX = 15;

function prevent () {
	for (let i = 1; i < 7; ++i) {
		const input = $(`#inputBox${i}`);
		input.on("change", function (e) {
			let num = parseInt(this.value);
			if (isNaN(num)) {
				this.value = 8;
			} else {
				this.value = Math.max(Math.min(num, STATS_MAX), STATS_MIN);
			}
			changeTotal();
		})
	}
}

window.onhashchange = function hashchange () {
	const hash = window.location.hash.slice(1);
	$(".statmethod").hide();
	$("#" + hash).show();
};

function getCost (n) {
	if (n < 14) return n - 8;
	if (n === 14) return 7;
	return 9
}

function choose () {
	if ($("input.choose:checked").length > count) return this.checked = false;

	$(".racial", this.parentNode.parentNode)
		.val(this.checked ? amount : 0);
	changeTotal()
}

function changeRace () {
	const race = this.value;
	const stats = raceData
		.find(({name}) => name === race).ability;

	$(".racial").val(0);
	for (const key in stats) $(`#${key} .racial`).val(stats[key])

	changeTotal();
	$(".choose").hide().prop("checked", false);

	if (!stats.choose) return;

	const {from} = stats.choose[0];
	amount = stats.choose[0].amount || 1;
	count = stats.choose[0].count;

	$("td.choose").text(`Choose ${count}`).show();
	from.forEach(key => $(`#${key} .choose`).show())
}

function changeTotal () {
	$("#pointbuy tr[id]").each((i, el) => {
		const [base, racial, total, mod] = $("input", el).get();
		const raw = total.value = Number(base.value) + Number(racial.value);
		mod.value = Math.floor((raw - 10) / 2)
	})
}

function changeBase (e) {
	const budget = Number($("#budget").val());

	let cost = 0;
	$(".base").each((i, el) => cost += getCost(Number(el.value)));

	if (cost > budget) return this.value;

	$("#remaining").val(budget - cost);

	changeTotal()
}

function rollstats () {
	var rolls = [];
	for (var i = 0; i < 6; i++) {
		var curroll = droll.roll("4d6").rolls.sort().splice(1);
		curroll = curroll[0] + curroll[1] + curroll[2];
		rolls.push(curroll);
	}

	$("#rolled #rolls").prepend("
" + rolls.join(", ") + "
");
	$("#rolled #rolls p:eq(10)").remove();
}
<|endoftext|>


<commit_before>
google.load('search', '1');

function player_init(){
  var width = (($(document).width())>1280? 1280: $(document).width());
  $.app.canvas = $('#seeker').get(0);
  $.app.ctx = $.app.canvas.getContext('2d');
  $('body').css('width', width);
  $('#player').get(0).volume = 0.1;
  $.app.playerAction = 'play';

  $('#player_wrapper #album_artwork').attr('src', '/images/nothumb.png');
  google.search.Search.getBranding('google_branding');
}

function loadMusic(files) {
  var musicFiles = _(files).select(function (file) {
    var path = file.webkitRelativePath || file.mozFullPath;
    var fileName = file.fileName;
    var size = file.fileSize;
    var lpath = path.toLowerCase();
    return !/\.$/.test(lpath) &&
      (/\.mp3$/.test(lpath)) ||
      (/\.ogg$/.test(lpath)) ||
      (/\.m4a$/.test(lpath))
  });

  var total = musicFiles.length, progress = 0;

  function incProgress() {
    progress++;
    $('#addmusic').html(progress+' / '+total);
    if(progress == total) {
      $.app.Storage.save();
    } else {
      newMuFile(musicFiles[progress]);
    }
  }

  $.app.liveFiles = {};

  function newMuFile(f) {
    $.app.liveFiles[f.webkitRelativePath] = f;
    var muFile = new $.app.MuFile(f, incProgress);
  }

  newMuFile(musicFiles[progress]);
}

function getPrettySongName(muFile) {
  var name = muFile.name.length > muFile.title.length ?
    muFile.name : muFile.title;
  return name.replace(/\.\w+$/,'');
}

function getYoutubeSearchURL(muFile) {
  var name = getPrettySongName(muFile);
  return 'http://www.youtube.com/results?search_query='+
    name.split(/\s+/).join('+')+'+'+
    muFile.artist.split(/\s+/).join('+');
}

function getLyricsSearchURL(muFile) {
  var name = getPrettySongName(muFile);
  return 'http://www.google.com/search?q='+
    name.split(/\s+/).join('+')+'+'+
    muFile.artist.split(/\s+/).join('+')+'+lyrics';
}

function getArtistWikipediaURL(muFile) {
  return 'http://en.wikipedia.org/w/index.php?search='+
          muFile.artist.split(/\s+/).join('+');
}

function getAmazonLink(muFile) {
  return 'http://www.amazon.com/s?ie=UTF8&x=0&ref_=nb_sb_noss&y=0&field-keywords='+encodeURI(muFile.artist)+'&url=search-alias%3Ddigital-music&_encoding=UTF8&tag=myfreq-20&linkCode=ur2&camp=1789&creative=390957'
}

function getSongEntryHtml(muFile, asSearchResult) {
  var name = getPrettySongName(muFile);
  var entryHTML = '
'+
    '  '+
    ''+getPrettySongName(muFile)+''+
    '  '+
    ''+muFile.album+''+
    '  '+
    ''+muFile.artist+''+
    '  '+
    (asSearchResult ?
      '+' :
      '-'+
      '>')+
  '
';
  var entry = $(entryHTML);

  entry
    .hover(
      function () { $(this).addClass('focused_entry'); },
      function () { $(this).removeClass('focused_entry'); }
    )
    .find('.entry_action').data('muFile',muFile);

  return entry;
}

function getObjectURL(path) {
  if($.app.liveFiles) {
    var liveFile = $.app.liveFiles[path];
    return liveFile ? window.webkitURL.createObjectURL(liveFile) : null;
  } else {
    return null;
  }
}

function searchImage(query, callback) {
  function onComplete() {
    if (imageSearch.results && imageSearch.results.length > 0) {
      var results = imageSearch.results;
      callback(results[0].tbUrl);
    }
  }

  var imageSearch = new google.search.ImageSearch();
  imageSearch.setSearchCompleteCallback(this, onComplete, null);
  imageSearch.execute(query);
}

function updateSongInfo(muFile){
  var songInfo = $('#player_column #player_controls #song_info');
  $('#title', songInfo).text(muFile.title).attr('title', muFile.title);
  $('#artist', songInfo).text(muFile.artist).attr('title', muFile.artist);
  $('#album', songInfo).text(muFile.album).attr('title', muFile.album);
}

var currentPlaying = null;
$.app.nextSong = function () {
  var nextDiv = currentPlaying.next();
  if(nextDiv.length == 0) {
    nextDiv = currentPlaying.parent().children().first();
  }
  playMedia(nextDiv);
}

function animateSeeker() {
    var player = $('#player').get(0);

    var degree = (player.currentTime/player.duration)*360;

    $.app.ctx.beginPath();
    $.app.ctx.clearRect(0,0,$.app.canvas.width,$.app.canvas.height);
    $.app.ctx.strokeStyle = 'rgba(00, 194, 256, 0.9)';
    $.app.ctx.lineWidth = 14;
    $.app.ctx.arc(76,75,65,Math.PI*(270/180),Math.PI*(degree+270)/180);
    $.app.ctx.shadowOffsetX = 0;
    $.app.ctx.shadowOffsetY = 0;
    $.app.ctx.shadowBlur = 5;
    $.app.ctx.shadowColor = 'rgba(170,170,170,0.7)';
    $.app.ctx.stroke();

    $.app.ctx.closePath();

    setTimeout(animateSeeker, 1000);
}

function pauseMedia() {
  $('#player').get(0).pause();
}

function playMedia(div, resumeFlag) {
  if(!div){
    if($('#playlist', '#playlist_wrapper').is(':empty')){
      alert('No selction made.');
      return;
    } else {
      div = $('.entry', '#playlist').first();
      resumeFlag = false;
    }
  }
  var muFile = div.find('.entry_action').data('muFile');
  var url = getObjectURL(muFile.path);

  if(url) {
    if(!resumeFlag) { $('#player').get(0).src = url; }

    $('#player').get(0).play();
    currentPlaying = div;
    currentPlaying.parent().children().removeClass('active_entry');
    currentPlaying.addClass('active_entry');
    searchImage(muFile.album+' '+muFile.artist,function (tbUrl) {
      $('#player_wrapper #album_artwork').attr('src',tbUrl);
    });
    updateSongInfo(muFile);
  } else {
    alert('Add music again');
  }
}

$(document).ready(function () {
  player_init();
  $.app.Storage.load();

  $('#addmusic').click(function () {
    $('input[name=actual_addmusic]').click();
  });

  $('#play', '#player_column')
    .hover(
      function () {
        $('img', this)
          .attr('src', '/images/'+$.app.playerAction+'_hover.png');
      },
      function () {
        $('img', this)
          .attr('src', '/images/'+$.app.playerAction+'.png');
      }
    )
    .click(function () {
      if($.app.playerAction == 'play'){
        playMedia(currentPlaying, true);
      } else {
        pauseMedia();
      }
    });

  $('#prev', '#player_column')
    .hover(
      function (){
        $('img', this).attr('src', '/images/previous_hover.png');
      },
      function (){
        $('img', this).attr('src', '/images/previous.png');
      }
    )
    .click(function (){

    });

  $('#next', '#player_column')
    .hover(
      function (){
        $('img', this).attr('src', '/images/next_hover.png');
      },
      function (){
        $('img', this).attr('src', '/images/next.png');
      }
    )
    .click(function (){

    });

  $('#player', '#player_column')
    .bind('play', function () {
      $.app.playerAction = 'pause';
      $('img', '#play').attr('src', '/images/pause.png');
      animateSeeker();
    })
    .bind('pause', function () {
      $.app.playerAction = 'play';
      $('img', '#play').attr('src', '/images/play.png');
    });

  $('#player_volume').slider({
    'range' : 'min',
    'value' : 10,
    'min' : 0.0,
    'max' : 100,
    'slide' : function (e, ui){
      $('#player').get(0).volume = (ui.value)/100;
    }
  });
  $('input[name=search]').keyup(function (e) {

    var divResults = $('#search_results');
    divResults.empty();
    var text = $(this).val();

    if(!text || (text && text.length < 4)) { return; }

    var results = $.app.Storage.search($(this).val());
    _(results).each(function (muFile) {
      divResults.append(getSongEntryHtml(muFile, true));
    });

    divResults.find('.entry_action')
      .click(function () {
        var muFile = $(this).data('muFile');
        var player_entry = getSongEntryHtml(muFile, false);
        player_entry.find('.entry_action').click(function () {
          playMedia($(this).parent());
        });
        player_entry.find('.remove_action').click(function () {
          playMedia($(this).parent().remove());
        });
        $('#playlist_wrapper #playlist').append(player_entry);
      });

  })
  .focus(function (e){
    $(this).val('');
  })
  .focusout(function (e){
    if($(this).val() == ''){
      $(this).val('Search...');
    }
  });
});

<commit_msg>prev and next navigation<commit_after>
google.load('search', '1');

function player_init(){
  var width = (($(document).width())>1280? 1280: $(document).width());
  $.app.canvas = $('#seeker').get(0);
  $.app.ctx = $.app.canvas.getContext('2d');
  $('body').css('width', width);
  $('#player').get(0).volume = 0.1;
  $.app.playerAction = 'play';

  $('#player_wrapper #album_artwork').attr('src', '/images/nothumb.png');
  google.search.Search.getBranding('google_branding');
}

function loadMusic(files) {
  var musicFiles = _(files).select(function (file) {
    var path = file.webkitRelativePath || file.mozFullPath;
    var fileName = file.fileName;
    var size = file.fileSize;
    var lpath = path.toLowerCase();
    return !/\.$/.test(lpath) &&
      (/\.mp3$/.test(lpath)) ||
      (/\.ogg$/.test(lpath)) ||
      (/\.m4a$/.test(lpath))
  });

  var total = musicFiles.length, progress = 0;

  function incProgress() {
    progress++;
    $('#addmusic').html(progress+' / '+total);
    if(progress == total) {
      $.app.Storage.save();
    } else {
      newMuFile(musicFiles[progress]);
    }
  }

  $.app.liveFiles = {};

  function newMuFile(f) {
    $.app.liveFiles[f.webkitRelativePath] = f;
    var muFile = new $.app.MuFile(f, incProgress);
  }

  newMuFile(musicFiles[progress]);
}

function getPrettySongName(muFile) {
  var name = muFile.name.length > muFile.title.length ?
    muFile.name : muFile.title;
  return name.replace(/\.\w+$/,'');
}

function getYoutubeSearchURL(muFile) {
  var name = getPrettySongName(muFile);
  return 'http://www.youtube.com/results?search_query='+
    name.split(/\s+/).join('+')+'+'+
    muFile.artist.split(/\s+/).join('+');
}

function getLyricsSearchURL(muFile) {
  var name = getPrettySongName(muFile);
  return 'http://www.google.com/search?q='+
    name.split(/\s+/).join('+')+'+'+
    muFile.artist.split(/\s+/).join('+')+'+lyrics';
}

function getArtistWikipediaURL(muFile) {
  return 'http://en.wikipedia.org/w/index.php?search='+
          muFile.artist.split(/\s+/).join('+');
}

function getAmazonLink(muFile) {
  return 'http://www.amazon.com/s?ie=UTF8&x=0&ref_=nb_sb_noss&y=0&field-keywords='+encodeURI(muFile.artist)+'&url=search-alias%3Ddigital-music&_encoding=UTF8&tag=myfreq-20&linkCode=ur2&camp=1789&creative=390957'
}

function getSongEntryHtml(muFile, asSearchResult) {
  var name = getPrettySongName(muFile);
  var entryHTML = '
'+
    '  '+
    ''+getPrettySongName(muFile)+''+
    '  '+
    ''+muFile.album+''+
    '  '+
    ''+muFile.artist+''+
    '  '+
    (asSearchResult ?
      '+' :
      '-'+
      '>')+
  '
';
  var entry = $(entryHTML);

  entry
    .hover(
      function () { $(this).addClass('focused_entry'); },
      function () { $(this).removeClass('focused_entry'); }
    )
    .find('.entry_action').data('muFile',muFile);

  return entry;
}

function getObjectURL(path) {
  if($.app.liveFiles) {
    var liveFile = $.app.liveFiles[path];
    return liveFile ? window.webkitURL.createObjectURL(liveFile) : null;
  } else {
    return null;
  }
}

function searchImage(query, callback) {
  function onComplete() {
    if (imageSearch.results && imageSearch.results.length > 0) {
      var results = imageSearch.results;
      callback(results[0].tbUrl);
    }
  }

  var imageSearch = new google.search.ImageSearch();
  imageSearch.setSearchCompleteCallback(this, onComplete, null);
  imageSearch.execute(query);
}

function updateSongInfo(muFile){
  var songInfo = $('#player_column #player_controls #song_info');
  $('#title', songInfo).text(muFile.title).attr('title', muFile.title);
  $('#artist', songInfo).text(muFile.artist).attr('title', muFile.artist);
  $('#album', songInfo).text(muFile.album).attr('title', muFile.album);
}

var currentPlaying = null;
$.app.prevSong = function () {
  var prevDiv = currentPlaying.prev();
  if(prevDiv.length == 0) {
    prevDiv = currentPlaying.parent().children().last();
  }
  playMedia(prevDiv);
}

$.app.nextSong = function () {
  var nextDiv = currentPlaying.next();
  if(nextDiv.length == 0) {
    nextDiv = currentPlaying.parent().children().first();
  }
  playMedia(nextDiv);
}

function animateSeeker() {
    var player = $('#player').get(0);

    var degree = (player.currentTime/player.duration)*360;

    $.app.ctx.beginPath();
    $.app.ctx.clearRect(0,0,$.app.canvas.width,$.app.canvas.height);
    $.app.ctx.strokeStyle = 'rgba(00, 194, 256, 0.9)';
    $.app.ctx.lineWidth = 14;
    $.app.ctx.arc(76,75,65,Math.PI*(270/180),Math.PI*(degree+270)/180);
    $.app.ctx.shadowOffsetX = 0;
    $.app.ctx.shadowOffsetY = 0;
    $.app.ctx.shadowBlur = 5;
    $.app.ctx.shadowColor = 'rgba(170,170,170,0.7)';
    $.app.ctx.stroke();

    $.app.ctx.closePath();

    setTimeout(animateSeeker, 1000);
}

function pauseMedia() {
  $('#player').get(0).pause();
}

function playMedia(div, resumeFlag) {
  if(!div){
    if($('#playlist', '#playlist_wrapper').is(':empty')){
      alert('No selction made.');
      return;
    } else {
      div = $('.entry', '#playlist').first();
      resumeFlag = false;
    }
  }
  var muFile = div.find('.entry_action').data('muFile');
  var url = getObjectURL(muFile.path);

  if(url) {
    if(!resumeFlag) { $('#player').get(0).src = url; }

    $('#player').get(0).play();
    currentPlaying = div;
    currentPlaying.parent().children().removeClass('active_entry');
    currentPlaying.addClass('active_entry');
    searchImage(muFile.album+' '+muFile.artist,function (tbUrl) {
      $('#player_wrapper #album_artwork').attr('src',tbUrl);
    });
    updateSongInfo(muFile);
  } else {
    alert('Add music again');
  }
}

$(document).ready(function () {
  player_init();
  $.app.Storage.load();

  $('#addmusic').click(function () {
    $('input[name=actual_addmusic]').click();
  });

  $('#play', '#player_column')
    .hover(
      function () {
        $('img', this)
          .attr('src', '/images/'+$.app.playerAction+'_hover.png');
      },
      function () {
        $('img', this)
          .attr('src', '/images/'+$.app.playerAction+'.png');
      }
    )
    .click(function () {
      if($.app.playerAction == 'play'){
        playMedia(currentPlaying, true);
      } else {
        pauseMedia();
      }
    });

  $('#prev', '#player_column')
    .hover(
      function (){
        $('img', this).attr('src', '/images/previous_hover.png');
      },
      function (){
        $('img', this).attr('src', '/images/previous.png');
      }
    )
    .click(function (){
      $.app.prevSong();
    });

  $('#next', '#player_column')
    .hover(
      function (){
        $('img', this).attr('src', '/images/next_hover.png');
      },
      function (){
        $('img', this).attr('src', '/images/next.png');
      }
    )
    .click(function (){
      $.app.nextSong();
    });

  $('#player', '#player_column')
    .bind('play', function () {
      $.app.playerAction = 'pause';
      $('img', '#play').attr('src', '/images/pause.png');
      animateSeeker();
    })
    .bind('pause', function () {
      $.app.playerAction = 'play';
      $('img', '#play').attr('src', '/images/play.png');
    });

  $('#player_volume').slider({
    'range' : 'min',
    'value' : 10,
    'min' : 0.0,
    'max' : 100,
    'slide' : function (e, ui){
      $('#player').get(0).volume = (ui.value)/100;
    }
  });
  $('input[name=search]').keyup(function (e) {

    var divResults = $('#search_results');
    divResults.empty();
    var text = $(this).val();

    if(!text || (text && text.length < 4)) { return; }

    var results = $.app.Storage.search($(this).val());
    _(results).each(function (muFile) {
      divResults.append(getSongEntryHtml(muFile, true));
    });

    divResults.find('.entry_action')
      .click(function () {
        var muFile = $(this).data('muFile');
        var player_entry = getSongEntryHtml(muFile, false);
        player_entry.find('.entry_action').click(function () {
          playMedia($(this).parent());
        });
        player_entry.find('.remove_action').click(function () {
          playMedia($(this).parent().remove());
        });
        $('#playlist_wrapper #playlist').append(player_entry);
      });

  })
  .focus(function (e){
    $(this).val('');
  })
  .focusout(function (e){
    if($(this).val() == ''){
      $(this).val('Search...');
    }
  });
});

<|endoftext|>


<commit_before>Package.describe({
  summary: "Makes your Cordova application use the Crosswalk WebView \
instead of the System WebView on Android",
  version: '1.3.1',
  documentation: null
});

Cordova.depends({
  'cordova-plugin-crosswalk-webview': '1.3.1'
});
<commit_msg>Update Crosswalk to 1.4.0<commit_after>Package.describe({
  summary: "Makes your Cordova application use the Crosswalk WebView \
instead of the System WebView on Android",
  version: '1.4.0',
  documentation: null
});

Cordova.depends({
  'cordova-plugin-crosswalk-webview': '1.4.0'
});
<|endoftext|>


<commit_before>/**
 * @fileoverview game-shim - Shims to normalize gaming-related APIs to their respective specs
 * @author Brandon Jones
 * @version 0.7
 */

/*
 * Copyright (c) 2011 Brandon Jones
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 *    1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 *
 *    2. Altered source versions must be plainly marked as such, and must not
 *    be misrepresented as being the original software.
 *
 *    3. This notice may not be removed or altered from any source
 *    distribution.
 */

(function(global) {
    "use strict";

    var elementPrototype = (global.HTMLElement || global.Element)["prototype"];
    var getter;

    var GameShim = global.GameShim = {
        supports: {
            fullscreen: true,
            pointerLock: true,
            gamepad: true,
            highResTimer: true
        }
    };

    //=====================
    // Animation
    //=====================

    // window.requestAnimaionFrame, credit: Erik Moller
    // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
    (function() {
        var lastTime = 0;
        var vendors = ["webkit", "moz", "ms", "o"];
        var x;

        for(x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
            window.requestAnimationFrame = window[vendors[x]+"RequestAnimationFrame"];
        }

        window.cancelAnimationFrame = window.cancelAnimationFrame || window.cancelRequestAnimationFrame; // Check for older syntax
        for(x = 0; x < vendors.length && !window.cancelAnimationFrame; ++x) {
            window.cancelAnimationFrame = window[vendors[x]+"CancelAnimationFrame"] || window[vendors[x]+"CancelRequestAnimationFrame"];
        }

        // Manual fallbacks
        if (!window.requestAnimationFrame) {
            window.requestAnimationFrame = function(callback, element) {
                var currTime = Date.now();
                var timeToCall = Math.max(0, 16 - (currTime - lastTime));
                var id = window.setTimeout(function() { callback(currTime + timeToCall); },
                  timeToCall);
                lastTime = currTime + timeToCall;
                return id;
            };
        }

        if (!window.cancelAnimationFrame) {
            window.cancelAnimationFrame = function(id) {
                clearTimeout(id);
            };
        }

        // window.animationStartTime
        if(!window.animationStartTime) {
            getter = (function() {
                for(x = 0; x < vendors.length; ++x) {
                    if(window[vendors[x] + "AnimationStartTime"]) {
                        return function() { return window[vendors[x] + "AnimationStartTime"]; };
                    }
                }

                return function() { return Date.now(); };
            })();

            Object.defineProperty(window, "animationStartTime", {
                enumerable: true, configurable: false, writeable: false,
                get: getter
            });
        }
    }());

    //=====================
    // Fullscreen
    //=====================

    // document.fullscreenEnabled
    if(!document.hasOwnProperty("fullscreenEnabled")) {
        getter = (function() {
            // These are the functions that match the spec, and should be preferred
            if("webkitIsFullScreen" in document) {
                return function() { return webkitRequestFullScreen in document; };
            }
            if("mozFullScreenEnabled" in document) {
                return function() { return document.mozFullScreenEnabled; };
            }

            GameShim.supports.fullscreen = false;
            return function() { return false; }; // not supported, never fullscreen
        })();

        Object.defineProperty(document, "fullscreenEnabled", {
            enumerable: true, configurable: false, writeable: false,
            get: getter
        });
    }

    if(!document.hasOwnProperty("fullscreenElement")) {
        getter = (function() {
            // These are the functions that match the spec, and should be preferred
            if("webkitFullscreenElement" in document) {
                return function() { return document.webkitFullscreenElement; };
            }
            if("mozFullscreenElement" in document) {
                return function() { return document.mozFullscreenElement; };
            }
            return function() { return null; }; // not supported
        })();

        Object.defineProperty(document, "fullscreenElement", {
            enumerable: true, configurable: false, writeable: false,
            get: getter
        });
    }

    // Document event: fullscreenchange
    function fullscreenchange(oldEvent) {
        var newEvent = document.createEvent("CustomEvent");
        newEvent.initCustomEvent("fullscreenchange", true, false, null);
        // TODO: Any need for variable copy?
        document.dispatchEvent(newEvent);
    }
    document.addEventListener("webkitfullscreenchange", fullscreenchange, false);
    document.addEventListener("mozfullscreenchange", fullscreenchange, false);

    // Document event: fullscreenerror
    function fullscreenerror(oldEvent) {
        var newEvent = document.createEvent("CustomEvent");
        newEvent.initCustomEvent("fullscreenerror", true, false, null);
        // TODO: Any need for variable copy?
        document.dispatchEvent(newEvent);
    }
    document.addEventListener("webkitfullscreenerror", fullscreenerror, false);
    document.addEventListener("mozfullscreenerror", fullscreenerror, false);

    // element.requestFullScreen
    if(!elementPrototype.requestFullScreen) {
        elementPrototype.requestFullScreen = (function() {
            if(elementPrototype.webkitRequestFullScreen) {
                return function() {
                    this.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
                };
            }

            if(elementPrototype.mozRequestFullScreen) {
                return function() {
                    this.mozRequestFullScreen();
                };
            }

            return function(){ /* unsupported, fail silently */ };
        })();
    }

    // document.exitFullscreen
    if(!document.exitFullscreen) {
        document.exitFullscreen = (function() {
            return  document.webkitExitFullscreen ||
                    document.mozExitFullscreen ||
                    function(){ /* unsupported, fail silently */ };
        })();
    }

    //=====================
    // Pointer Lock
    //=====================

    var mouseEventPrototype = global.MouseEvent.prototype;

    if(!("movementX" in mouseEventPrototype)) {
        Object.defineProperty(mouseEventPrototype, "movementX", {
            enumerable: true, configurable: false, writeable: false,
            get: function() { return this.webkitMovementX || this.mozMovementX || 0; }
        });
    }

    if(!("movementY" in mouseEventPrototype)) {
        Object.defineProperty(mouseEventPrototype, "movementY", {
            enumerable: true, configurable: false, writeable: false,
            get: function() { return this.webkitMovementY || this.mozMovementY || 0; }
        });
    }

    // Navigator pointer is not the right interface according to spec.
    // Here for backwards compatibility only
    if(!navigator.pointer) {
        navigator.pointer = navigator.webkitPointer || navigator.mozPointer;
    }

    // Document event: pointerlockchange
    function pointerlockchange(oldEvent) {
        var newEvent = document.createEvent("CustomEvent");
        newEvent.initCustomEvent("pointerlockchange", true, false, null);
        document.dispatchEvent(newEvent);
    }
    document.addEventListener("webkitpointerlockchange", pointerlockchange, false);
    document.addEventListener("webkitpointerlocklost", pointerlockchange, false);
    document.addEventListener("mozpointerlockchange", pointerlockchange, false);
    document.addEventListener("mozpointerlocklost", pointerlockchange, false);

    // Document event: pointerlockerror
    function pointerlockerror(oldEvent) {
        var newEvent = document.createEvent("CustomEvent");
        newEvent.initCustomEvent("pointerlockerror", true, false, null);
        document.dispatchEvent(newEvent);
    }
    document.addEventListener("webkitpointerlockerror", pointerlockerror, false);
    document.addEventListener("mozpointerlockerror", pointerlockerror, false);

    if(!document.hasOwnProperty("pointerLockElement")) {
        getter = (function() {
            // These are the functions that match the spec, and should be preferred
            if("webkitPointerLockElement" in document) {
                return function() { return document.webkitPointerLockElement; };
            }
            if("mozPointerLockElement" in document) {
                return function() { return document.mozPointerLockElement; };
            }
            return function() { return null; }; // not supported
        })();

        Object.defineProperty(document, "pointerLockElement", {
            enumerable: true, configurable: false, writeable: false,
            get: getter
        });
    }

    // element.requestPointerLock
    if(!elementPrototype.requestPointerLock) {
        elementPrototype.requestPointerLock = (function() {
            if(elementPrototype.webkitRequestPointerLock) {
                return function() {
                    this.webkitRequestPointerLock();
                };
            }

            if(elementPrototype.mozRequestPointerLock) {
                return function() {
                    this.mozRequestPointerLock();
                };
            }

            if(navigator.pointer) {
                return function() {
                    var elem = this;
                    navigator.pointer.lock(elem, pointerlockchange, pointerlockerror);
                };
            }

            GameShim.supports.pointerLock = false;

            return function(){}; // not supported
        })();
    }

    // document.exitPointerLock
    if(!document.exitPointerLock) {
        document.exitPointerLock = (function() {
            return  document.webkitExitPointerLock ||
                    document.mozExitPointerLock ||
                    function(){
                        if(navigator.pointer) {
                            var elem = this;
                            navigator.pointer.unlock();
                        }
                    };
        })();
    }

    //=====================
    // Gamepad
    //=====================

    if(!navigator.gamepads) {
        getter = (function() {
            // These are the functions that match the spec, and should be preferred
            if("webkitGamepads" in navigator) {
                return function() { return navigator.webkitGamepads; };
            }
            if("mozGamepads" in navigator) {
                return function() { return navigator.mozGamepads; };
            }

            GameShim.supports.gamepad = false;
            var gamepads = [];
            return function() { return gamepads; }; // not supported, return empty array
        })();

        Object.defineProperty(navigator, "gamepads", {
            enumerable: true, configurable: false, writeable: false,
            get: getter
        });
    }

    //=======================
    // High Resolution Timer
    //=======================

    if(!window.performance) {
        window.performance = {};
    }

    if(!window.performance.timing) {
        window.performance.timing = {
            navigationStart: Date.now() // Terrible approximation, I know. Sorry.
        };
    }

    if(!window.performance.now) {
        window.performance.now = (function() {
            // FYI: Mozilla supports high-res timers without prefixes.

            if(window.performance.webkitNow) {
                return window.performance.webkitNow;
            }

            GameShim.supports.highResTimer = false;

            return function(){ // not supported, return a low-resolution approximation
                return Date.now() - window.performance.timing.navigationStart;
            };
        })();
    }

})((typeof(exports) != 'undefined') ? global : window); // Account for CommonJS environments<commit_msg>Spelling and case sensitivity corrections to make the shim work properly in Firefox.<commit_after>/**
 * @fileoverview game-shim - Shims to normalize gaming-related APIs to their respective specs
 * @author Brandon Jones
 * @version 0.7
 */

/*
 * Copyright (c) 2011 Brandon Jones
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 *    1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 *
 *    2. Altered source versions must be plainly marked as such, and must not
 *    be misrepresented as being the original software.
 *
 *    3. This notice may not be removed or altered from any source
 *    distribution.
 */

(function(global) {
    "use strict";

    var elementPrototype = (global.HTMLElement || global.Element)["prototype"];
    var getter;

    var GameShim = global.GameShim = {
        supports: {
            fullscreen: true,
            pointerLock: true,
            gamepad: true,
            highResTimer: true
        }
    };

    //=====================
    // Animation
    //=====================

    // window.requestAnimaionFrame, credit: Erik Moller
    // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
    (function() {
        var lastTime = 0;
        var vendors = ["webkit", "moz", "ms", "o"];
        var x;

        for(x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
            window.requestAnimationFrame = window[vendors[x]+"RequestAnimationFrame"];
        }

        window.cancelAnimationFrame = window.cancelAnimationFrame || window.cancelRequestAnimationFrame; // Check for older syntax
        for(x = 0; x < vendors.length && !window.cancelAnimationFrame; ++x) {
            window.cancelAnimationFrame = window[vendors[x]+"CancelAnimationFrame"] || window[vendors[x]+"CancelRequestAnimationFrame"];
        }

        // Manual fallbacks
        if (!window.requestAnimationFrame) {
            window.requestAnimationFrame = function(callback, element) {
                var currTime = Date.now();
                var timeToCall = Math.max(0, 16 - (currTime - lastTime));
                var id = window.setTimeout(function() { callback(currTime + timeToCall); },
                  timeToCall);
                lastTime = currTime + timeToCall;
                return id;
            };
        }

        if (!window.cancelAnimationFrame) {
            window.cancelAnimationFrame = function(id) {
                clearTimeout(id);
            };
        }

        // window.animationStartTime
        if(!window.animationStartTime) {
            getter = (function() {
                for(x = 0; x < vendors.length; ++x) {
                    if(window[vendors[x] + "AnimationStartTime"]) {
                        return function() { return window[vendors[x] + "AnimationStartTime"]; };
                    }
                }

                return function() { return Date.now(); };
            })();

            Object.defineProperty(window, "animationStartTime", {
                enumerable: true, configurable: false, writeable: false,
                get: getter
            });
        }
    }());

    //=====================
    // Fullscreen
    //=====================

    // document.fullscreenEnabled
    if(!document.hasOwnProperty("fullscreenEnabled")) {
        getter = (function() {
            // These are the functions that match the spec, and should be preferred
            if("webkitIsFullScreen" in document) {
                return function() { return webkitRequestFullScreen in document; };
            }
            if("mozFullScreenEnabled" in document) {
                return function() { return document.mozFullScreenEnabled; };
            }

            GameShim.supports.fullscreen = false;
            return function() { return false; }; // not supported, never fullscreen
        })();

        Object.defineProperty(document, "fullscreenEnabled", {
            enumerable: true, configurable: false, writeable: false,
            get: getter
        });
    }

    if(!document.hasOwnProperty("fullscreenElement")) {
        getter = (function() {
            // These are the functions that match the spec, and should be preferred
            if("webkitFullscreenElement" in document) {
                return function() { return document.webkitFullscreenElement; };
            }
            if("mozFullScreenElement" in document) {
                return function() { return document.mozFullScreenElement; };
            }
            return function() { return null; }; // not supported
        })();

        Object.defineProperty(document, "fullscreenElement", {
            enumerable: true, configurable: false, writeable: false,
            get: getter
        });
    }

    // Document event: fullscreenchange
    function fullscreenchange(oldEvent) {
        var newEvent = document.createEvent("CustomEvent");
        newEvent.initCustomEvent("fullscreenchange", true, false, null);
        // TODO: Any need for variable copy?
        document.dispatchEvent(newEvent);
    }
    document.addEventListener("webkitfullscreenchange", fullscreenchange, false);
    document.addEventListener("mozfullscreenchange", fullscreenchange, false);

    // Document event: fullscreenerror
    function fullscreenerror(oldEvent) {
        var newEvent = document.createEvent("CustomEvent");
        newEvent.initCustomEvent("fullscreenerror", true, false, null);
        // TODO: Any need for variable copy?
        document.dispatchEvent(newEvent);
    }
    document.addEventListener("webkitfullscreenerror", fullscreenerror, false);
    document.addEventListener("mozfullscreenerror", fullscreenerror, false);

    // element.requestFullScreen
    if(!elementPrototype.requestFullscreen) {
        elementPrototype.requestFullscreen = (function() {
            if(elementPrototype.webkitRequestFullScreen) {
                return function() {
                    this.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
                };
            }

            if(elementPrototype.mozRequestFullScreen) {
                return function() {
                    this.mozRequestFullScreen();
                };
            }

            return function(){ /* unsupported, fail silently */ };
        })();
    }

    // document.exitFullscreen
    if(!document.exitFullscreen) {
        document.exitFullscreen = (function() {
            return  document.webkitExitFullscreen ||
                    document.mozCancelFullScreen ||
                    function(){ /* unsupported, fail silently */ };
        })();
    }

    //=====================
    // Pointer Lock
    //=====================

    var mouseEventPrototype = global.MouseEvent.prototype;

    if(!("movementX" in mouseEventPrototype)) {
        Object.defineProperty(mouseEventPrototype, "movementX", {
            enumerable: true, configurable: false, writeable: false,
            get: function() { return this.webkitMovementX || this.mozMovementX || 0; }
        });
    }

    if(!("movementY" in mouseEventPrototype)) {
        Object.defineProperty(mouseEventPrototype, "movementY", {
            enumerable: true, configurable: false, writeable: false,
            get: function() { return this.webkitMovementY || this.mozMovementY || 0; }
        });
    }

    // Navigator pointer is not the right interface according to spec.
    // Here for backwards compatibility only
    if(!navigator.pointer) {
        navigator.pointer = navigator.webkitPointer || navigator.mozPointer;
    }

    // Document event: pointerlockchange
    function pointerlockchange(oldEvent) {
        var newEvent = document.createEvent("CustomEvent");
        newEvent.initCustomEvent("pointerlockchange", true, false, null);
        document.dispatchEvent(newEvent);
    }
    document.addEventListener("webkitpointerlockchange", pointerlockchange, false);
    document.addEventListener("webkitpointerlocklost", pointerlockchange, false);
    document.addEventListener("mozpointerlockchange", pointerlockchange, false);
    document.addEventListener("mozpointerlocklost", pointerlockchange, false);

    // Document event: pointerlockerror
    function pointerlockerror(oldEvent) {
        var newEvent = document.createEvent("CustomEvent");
        newEvent.initCustomEvent("pointerlockerror", true, false, null);
        document.dispatchEvent(newEvent);
    }
    document.addEventListener("webkitpointerlockerror", pointerlockerror, false);
    document.addEventListener("mozpointerlockerror", pointerlockerror, false);

    if(!document.hasOwnProperty("pointerLockElement")) {
        getter = (function() {
            // These are the functions that match the spec, and should be preferred
            if("webkitPointerLockElement" in document) {
                return function() { return document.webkitPointerLockElement; };
            }
            if("mozPointerLockElement" in document) {
                return function() { return document.mozPointerLockElement; };
            }
            return function() { return null; }; // not supported
        })();

        Object.defineProperty(document, "pointerLockElement", {
            enumerable: true, configurable: false, writeable: false,
            get: getter
        });
    }

    // element.requestPointerLock
    if(!elementPrototype.requestPointerLock) {
        elementPrototype.requestPointerLock = (function() {
            if(elementPrototype.webkitRequestPointerLock) {
                return function() {
                    this.webkitRequestPointerLock();
                };
            }

            if(elementPrototype.mozRequestPointerLock) {
                return function() {
                    this.mozRequestPointerLock();
                };
            }

            if(navigator.pointer) {
                return function() {
                    var elem = this;
                    navigator.pointer.lock(elem, pointerlockchange, pointerlockerror);
                };
            }

            GameShim.supports.pointerLock = false;

            return function(){}; // not supported
        })();
    }

    // document.exitPointerLock
    if(!document.exitPointerLock) {
        document.exitPointerLock = (function() {
            return  document.webkitExitPointerLock ||
                    document.mozExitPointerLock ||
                    function(){
                        if(navigator.pointer) {
                            var elem = this;
                            navigator.pointer.unlock();
                        }
                    };
        })();
    }

    //=====================
    // Gamepad
    //=====================

    if(!navigator.gamepads) {
        getter = (function() {
            // These are the functions that match the spec, and should be preferred
            if("webkitGamepads" in navigator) {
                return function() { return navigator.webkitGamepads; };
            }
            if("mozGamepads" in navigator) {
                return function() { return navigator.mozGamepads; };
            }

            GameShim.supports.gamepad = false;
            var gamepads = [];
            return function() { return gamepads; }; // not supported, return empty array
        })();

        Object.defineProperty(navigator, "gamepads", {
            enumerable: true, configurable: false, writeable: false,
            get: getter
        });
    }

    //=======================
    // High Resolution Timer
    //=======================

    if(!window.performance) {
        window.performance = {};
    }

    if(!window.performance.timing) {
        window.performance.timing = {
            navigationStart: Date.now() // Terrible approximation, I know. Sorry.
        };
    }

    if(!window.performance.now) {
        window.performance.now = (function() {
            // FYI: Mozilla supports high-res timers without prefixes.

            if(window.performance.webkitNow) {
                return window.performance.webkitNow;
            }

            GameShim.supports.highResTimer = false;

            return function(){ // not supported, return a low-resolution approximation
                return Date.now() - window.performance.timing.navigationStart;
            };
        })();
    }

})((typeof(exports) != 'undefined') ? global : window); // Account for CommonJS environments<|endoftext|>