<commit_before>/**
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
*/

/* jshint sub:true, laxcomma:true, laxbreak:true */

/*
A class for holidng the information currently stored in plugin.xml
It should also be able to answer questions like whether the plugin
is compatible with a given engine version.

TODO (kamrik): refactor this to not use sync functions and return promises.
*/

var path = require('path')
  , xml_helpers = require('./util/xml-helpers')
  , CordovaError = require('./CordovaError')
  ;

function PluginInfo(dirname) {
    var self = this;

    // METHODS
    // Defined inside the constructor to avoid the "this" binding problems.

    // <preference> tag
    // Example: <preference name="API_KEY" />
    // Used to require a variable to be specified via --variable when installing the plugin.
    self.getPreferences = getPreferences;
    function getPreferences(platform) {
        var prefs = _getTags(self._et, 'preference', platform, _parsePreference);
        return prefs;
    }

    function _parsePreference(prefTag) {
        var pref = prefTag.attrib.name.toUpperCase();
        return pref;
    }

    // <asset>
    self.getAssets = getAssets;
    function getAssets(platform) {
        var assets = _getTags(self._et, 'asset', platform, _parseAsset);
        return assets;
    }

    function _parseAsset(tag) {
        var src = tag.attrib.src;
        var target = tag.attrib.target;

        if ( !src || !target) {
            var msg =
                'Malformed <asset> tag. Both "src" and "target" attributes'
                + 'must be specified in\n'
                + self.filepath
                ;
            throw new Error(msg);
        }

        var asset = { src: src, target: target };
        return asset;
    }

    // <dependency>
    // Example:
    // <dependency id="com.plugin.id"
    //     url="https://github.com/myuser/someplugin"
    //     commit="428931ada3891801"
    //     subdir="some/path/here" />
    self.getDependencies = getDependencies;
    function getDependencies(platform) {
        var deps = _getTags(
                self._et,
                'dependency',
                platform,
                _parseDependency
        );
        return deps;
    }

    function _parseDependency(tag) {
        var dep =
            { id : tag.attrib.id
            , url : tag.attrib.url || ''
            , subdir : tag.attrib.subdir || ''
            , commit : tag.attrib.commit
            };

        dep.git_ref = dep.commit;

        if ( !dep.id ) {
            var msg =
                '<dependency> tag is missing id attribute in '
                + self.filepath
                ;
            throw new CordovaError(msg);
        }
        return dep;
    }

    // <config-file> tag
    self.getConfigFiles = getConfigFiles;
    function getConfigFiles(platform) {
        var configFiles = _getTags(self._et, 'config-file', platform, _parseConfigFile);
        return configFiles;
    }

    function _parseConfigFile(tag) {
        var configFile =
            { target : tag.attrib['target']
            , parent : tag.attrib['parent']
            , after : tag.attrib['after']
            , xmls : tag.getchildren()
            // To support demuxing via versions
            , versions : tag.attrib['versions']
            , deviceTarget: tag.attrib['device-target']
            };
        return configFile;
    }

    // <info> tags, both global and within a <platform>
    // TODO (kamrik): Do we ever use <info> under <platform>? Example wanted.
    self.getInfo = getInfo;
    function getInfo(platform) {
        var infos = _getTags(
                self._et,
                'info',
                platform,
                function(elem) { return elem.text; }
        );
        // Filter out any undefined or empty strings.
        infos = infos.filter(Boolean);
        return infos;
    }

    // <source-file>
    // Examples:
    // <source-file src="src/ios/someLib.a" framework="true" />
    // <source-file src="src/ios/someLib.a" compiler-flags="-fno-objc-arc" />
    self.getSourceFiles = getSourceFiles;
    function getSourceFiles(platform) {
        var sourceFiles = _getTagsInPlatform(self._et, 'source-file', platform, _parseSourceFile);
        return sourceFiles;
    }

    function _parseSourceFile(tag) {
        return {
            itemType: 'source-file',
            src: tag.attrib.src,
            framework: isStrTrue(tag.attrib.framework),
            weak: isStrTrue(tag.attrib.weak),
            compilerFlags: tag.attrib['compiler-flags'],
            targetDir: tag.attrib['target-dir']
        };
    }

    // <header-file>
    // Example:
    // <header-file src="CDVFoo.h" />
    self.getHeaderFiles = getHeaderFiles;
    function getHeaderFiles(platform) {
        var headerFiles = _getTagsInPlatform(self._et, 'header-file', platform, function(tag) {
            return {
                itemType: 'header-file',
                src: tag.attrib.src,
                targetDir: tag.attrib['target-dir']
            };
        });
        return headerFiles;
    }

    // <resource-file>
    // Example:
    // <resource-file src="FooPluginStrings.xml" target="res/values/FooPluginStrings.xml" />
    self.getResourceFiles = getResourceFiles;
    function getResourceFiles(platform) {
        var resourceFiles = _getTagsInPlatform(self._et, 'resource-file', platform, function(tag) {
            return {
                itemType: 'resource-file',
                src: tag.attrib.src,
                target: tag.attrib.target
            };
        });
        return resourceFiles;
    }

    // <lib-file>
    // Example:
    // <lib-file src="src/BlackBerry10/native/device/libfoo.so" arch="device" />
    self.getLibFiles = getLibFiles;
    function getLibFiles(platform) {
        var libFiles = _getTagsInPlatform(self._et, 'lib-file', platform, function(tag) {
            return {
                itemType: 'lib-file',
                src: tag.attrib.src,
                arch: tag.attrib.arch,
                Include: tag.attrib.Include,
                versions: tag.attrib.versions,
                deviceTarget: tag.attrib['device-target'] || tag.attrib.target
            };
        });
        return libFiles;
    }

    // <hook>
    // Example:
    // <hook type="before_build" src="scripts/beforeBuild.js" />
    self.getHookScripts = getHookScripts;
    function getHookScripts(hook, platforms) {
        var scriptElements =  self._et.findall('./hook');

        if(platforms) {
            platforms.forEach(function (platform) {
                scriptElements = scriptElements.concat(self._et.findall('./platform[@name="' + platform + '"]/hook'));
            });
        }

        function filterScriptByHookType(el) {
            return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
        }

        return scriptElements.filter(filterScriptByHookType);
    }

    self.getJsModules = getJsModules;
    function getJsModules(platform) {
        var modules = _getTags(self._et, 'js-module', platform, _parseJsModule);
        return modules;
    }

    function _parseJsModule(tag) {
        var ret = {
            name: tag.attrib.name,
            src: tag.attrib.src,
            clobbers: tag.findall('clobbers').map(function(tag) { return { target: tag.attrib.target }; }),
            merges: tag.findall('merges').map(function(tag) { return { target: tag.attrib.target }; }),
            runs: tag.findall('runs').length > 0
        };

        return ret;
    }

    self.getEngines = function() {
        return self._et.findall('engines/engine').map(function(n) {
            return {
                name: n.attrib.name,
                version: n.attrib.version,
                platform: n.attrib.platform,
                scriptSrc: n.attrib.scriptSrc
            };
        });
    };

    self.getPlatforms = function() {
        return self._et.findall('platform').map(function(n) {
            return { name: n.attrib.name };
        });
    };

    self.getPlatformsArray = function() {
        return self._et.findall('platform').map(function(n) {
            return n.attrib.name;
        });
    };
    self.getFrameworks = function(platform) {
        return _getTags(self._et, 'framework', platform, function(el) {
            var ret = {
                itemType: 'framework',
                type: el.attrib.type,
                parent: el.attrib.parent,
                custom: isStrTrue(el.attrib.custom),
                src: el.attrib.src,
                weak: isStrTrue(el.attrib.weak),
                versions: el.attrib.versions,
                deviceTarget: el.attrib['device-target'] || el.attrib.target,
                arch: el.attrib.arch
            };
            return ret;
        });
    };

    self.getFilesAndFrameworks = getFilesAndFrameworks;
    function getFilesAndFrameworks(platform) {
        // Please avoid changing the order of the calls below, files will be
        // installed in this order.
        var items = [].concat(
            self.getSourceFiles(platform),
            self.getHeaderFiles(platform),
            self.getResourceFiles(platform),
            self.getFrameworks(platform),
            self.getLibFiles(platform)
        );
        return items;
    }
    ///// End of PluginInfo methods /////

    ///// PluginInfo Constructor logic  /////
    self.filepath = path.join(dirname, 'plugin.xml');

    self.dir = dirname;
    var et = self._et = xml_helpers.parseElementtreeSync(self.filepath);
    var pelem = et.getroot();
    self.id = pelem.attrib.id;
    self.version = pelem.attrib.version;

    // Optional fields
    self.name = pelem.findtext('name');
    self.description = pelem.findtext('description');
    self.license = pelem.findtext('license');
    self.repo = pelem.findtext('repo');
    self.issue = pelem.findtext('issue');
    self.keywords = pelem.findtext('keywords');
    self.info = pelem.findtext('info');
    if (self.keywords) {
        self.keywords = self.keywords.split(',').map( function(s) { return s.trim(); } );
    }
    self.getKeywordsAndPlatforms = function () {
        return self.keywords.concat('ecosystem:cordova').concat(addCordova(self.getPlatformsArray()));
    };
}  // End of PluginInfo constructor.

// Helper function used to prefix every element of an array with cordova-
// Useful when we want to modify platforms to be cordova-platform
function addCordova(someArray) {
    var newArray = someArray.map(function(element) {
        return 'cordova-' + element;
    });
    return newArray;
}

// Helper function used by most of the getSomething methods of PluginInfo.
// Get all elements of a given name. Both in root and in platform sections
// for the given platform. If transform is given and is a function, it is
// applied to each element.
function _getTags(pelem, tag, platform, transform) {
    var platformTag = pelem.find('./platform[@name="' + platform + '"]');
    if (platform == 'windows' && !platformTag) {
        platformTag = pelem.find('platform[@name="' + 'windows8' + '"]');
    }
    var tagsInRoot = pelem.findall(tag);
    tagsInRoot = tagsInRoot || [];
    var tagsInPlatform = platformTag ? platformTag.findall(tag) : [];
    var tags = tagsInRoot.concat(tagsInPlatform);
    if ( typeof transform === 'function' ) {
        tags = tags.map(transform);
    }
    return tags;
}

// Same as _getTags() but only looks inside a platfrom section.
function _getTagsInPlatform(pelem, tag, platform, transform) {
    var platformTag = pelem.find('./platform[@name="' + platform + '"]');
    if (platform == 'windows' && !platformTag) {
        platformTag = pelem.find('platform[@name="' + 'windows8' + '"]');
    }
    var tags = platformTag ? platformTag.findall(tag) : [];
    if ( typeof transform === 'function' ) {
        tags = tags.map(transform);
    }
    return tags;
}

// Check if x is a string 'true'.
function isStrTrue(x) {
    return String(x).toLowerCase() == 'true';
}

module.exports = PluginInfo;
// Backwards compat:
PluginInfo.PluginInfo = PluginInfo;
PluginInfo.loadPluginsDir = function(dir) {
    var PluginInfoProvider = require('./PluginInfoProvider');
    return new PluginInfoProvider().getAllWithinSearchPath(dir);
};
<commit_msg>Fix NPE in PluginInfo.getKeywordsAndPlatforms when no keywords exist<commit_after>/**
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
*/

/* jshint sub:true, laxcomma:true, laxbreak:true */

/*
A class for holidng the information currently stored in plugin.xml
It should also be able to answer questions like whether the plugin
is compatible with a given engine version.

TODO (kamrik): refactor this to not use sync functions and return promises.
*/

var path = require('path')
  , xml_helpers = require('./util/xml-helpers')
  , CordovaError = require('./CordovaError')
  ;

function PluginInfo(dirname) {
    var self = this;

    // METHODS
    // Defined inside the constructor to avoid the "this" binding problems.

    // <preference> tag
    // Example: <preference name="API_KEY" />
    // Used to require a variable to be specified via --variable when installing the plugin.
    self.getPreferences = getPreferences;
    function getPreferences(platform) {
        var prefs = _getTags(self._et, 'preference', platform, _parsePreference);
        return prefs;
    }

    function _parsePreference(prefTag) {
        var pref = prefTag.attrib.name.toUpperCase();
        return pref;
    }

    // <asset>
    self.getAssets = getAssets;
    function getAssets(platform) {
        var assets = _getTags(self._et, 'asset', platform, _parseAsset);
        return assets;
    }

    function _parseAsset(tag) {
        var src = tag.attrib.src;
        var target = tag.attrib.target;

        if ( !src || !target) {
            var msg =
                'Malformed <asset> tag. Both "src" and "target" attributes'
                + 'must be specified in\n'
                + self.filepath
                ;
            throw new Error(msg);
        }

        var asset = { src: src, target: target };
        return asset;
    }

    // <dependency>
    // Example:
    // <dependency id="com.plugin.id"
    //     url="https://github.com/myuser/someplugin"
    //     commit="428931ada3891801"
    //     subdir="some/path/here" />
    self.getDependencies = getDependencies;
    function getDependencies(platform) {
        var deps = _getTags(
                self._et,
                'dependency',
                platform,
                _parseDependency
        );
        return deps;
    }

    function _parseDependency(tag) {
        var dep =
            { id : tag.attrib.id
            , url : tag.attrib.url || ''
            , subdir : tag.attrib.subdir || ''
            , commit : tag.attrib.commit
            };

        dep.git_ref = dep.commit;

        if ( !dep.id ) {
            var msg =
                '<dependency> tag is missing id attribute in '
                + self.filepath
                ;
            throw new CordovaError(msg);
        }
        return dep;
    }

    // <config-file> tag
    self.getConfigFiles = getConfigFiles;
    function getConfigFiles(platform) {
        var configFiles = _getTags(self._et, 'config-file', platform, _parseConfigFile);
        return configFiles;
    }

    function _parseConfigFile(tag) {
        var configFile =
            { target : tag.attrib['target']
            , parent : tag.attrib['parent']
            , after : tag.attrib['after']
            , xmls : tag.getchildren()
            // To support demuxing via versions
            , versions : tag.attrib['versions']
            , deviceTarget: tag.attrib['device-target']
            };
        return configFile;
    }

    // <info> tags, both global and within a <platform>
    // TODO (kamrik): Do we ever use <info> under <platform>? Example wanted.
    self.getInfo = getInfo;
    function getInfo(platform) {
        var infos = _getTags(
                self._et,
                'info',
                platform,
                function(elem) { return elem.text; }
        );
        // Filter out any undefined or empty strings.
        infos = infos.filter(Boolean);
        return infos;
    }

    // <source-file>
    // Examples:
    // <source-file src="src/ios/someLib.a" framework="true" />
    // <source-file src="src/ios/someLib.a" compiler-flags="-fno-objc-arc" />
    self.getSourceFiles = getSourceFiles;
    function getSourceFiles(platform) {
        var sourceFiles = _getTagsInPlatform(self._et, 'source-file', platform, _parseSourceFile);
        return sourceFiles;
    }

    function _parseSourceFile(tag) {
        return {
            itemType: 'source-file',
            src: tag.attrib.src,
            framework: isStrTrue(tag.attrib.framework),
            weak: isStrTrue(tag.attrib.weak),
            compilerFlags: tag.attrib['compiler-flags'],
            targetDir: tag.attrib['target-dir']
        };
    }

    // <header-file>
    // Example:
    // <header-file src="CDVFoo.h" />
    self.getHeaderFiles = getHeaderFiles;
    function getHeaderFiles(platform) {
        var headerFiles = _getTagsInPlatform(self._et, 'header-file', platform, function(tag) {
            return {
                itemType: 'header-file',
                src: tag.attrib.src,
                targetDir: tag.attrib['target-dir']
            };
        });
        return headerFiles;
    }

    // <resource-file>
    // Example:
    // <resource-file src="FooPluginStrings.xml" target="res/values/FooPluginStrings.xml" />
    self.getResourceFiles = getResourceFiles;
    function getResourceFiles(platform) {
        var resourceFiles = _getTagsInPlatform(self._et, 'resource-file', platform, function(tag) {
            return {
                itemType: 'resource-file',
                src: tag.attrib.src,
                target: tag.attrib.target
            };
        });
        return resourceFiles;
    }

    // <lib-file>
    // Example:
    // <lib-file src="src/BlackBerry10/native/device/libfoo.so" arch="device" />
    self.getLibFiles = getLibFiles;
    function getLibFiles(platform) {
        var libFiles = _getTagsInPlatform(self._et, 'lib-file', platform, function(tag) {
            return {
                itemType: 'lib-file',
                src: tag.attrib.src,
                arch: tag.attrib.arch,
                Include: tag.attrib.Include,
                versions: tag.attrib.versions,
                deviceTarget: tag.attrib['device-target'] || tag.attrib.target
            };
        });
        return libFiles;
    }

    // <hook>
    // Example:
    // <hook type="before_build" src="scripts/beforeBuild.js" />
    self.getHookScripts = getHookScripts;
    function getHookScripts(hook, platforms) {
        var scriptElements =  self._et.findall('./hook');

        if(platforms) {
            platforms.forEach(function (platform) {
                scriptElements = scriptElements.concat(self._et.findall('./platform[@name="' + platform + '"]/hook'));
            });
        }

        function filterScriptByHookType(el) {
            return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
        }

        return scriptElements.filter(filterScriptByHookType);
    }

    self.getJsModules = getJsModules;
    function getJsModules(platform) {
        var modules = _getTags(self._et, 'js-module', platform, _parseJsModule);
        return modules;
    }

    function _parseJsModule(tag) {
        var ret = {
            name: tag.attrib.name,
            src: tag.attrib.src,
            clobbers: tag.findall('clobbers').map(function(tag) { return { target: tag.attrib.target }; }),
            merges: tag.findall('merges').map(function(tag) { return { target: tag.attrib.target }; }),
            runs: tag.findall('runs').length > 0
        };

        return ret;
    }

    self.getEngines = function() {
        return self._et.findall('engines/engine').map(function(n) {
            return {
                name: n.attrib.name,
                version: n.attrib.version,
                platform: n.attrib.platform,
                scriptSrc: n.attrib.scriptSrc
            };
        });
    };

    self.getPlatforms = function() {
        return self._et.findall('platform').map(function(n) {
            return { name: n.attrib.name };
        });
    };

    self.getPlatformsArray = function() {
        return self._et.findall('platform').map(function(n) {
            return n.attrib.name;
        });
    };
    self.getFrameworks = function(platform) {
        return _getTags(self._et, 'framework', platform, function(el) {
            var ret = {
                itemType: 'framework',
                type: el.attrib.type,
                parent: el.attrib.parent,
                custom: isStrTrue(el.attrib.custom),
                src: el.attrib.src,
                weak: isStrTrue(el.attrib.weak),
                versions: el.attrib.versions,
                deviceTarget: el.attrib['device-target'] || el.attrib.target,
                arch: el.attrib.arch
            };
            return ret;
        });
    };

    self.getFilesAndFrameworks = getFilesAndFrameworks;
    function getFilesAndFrameworks(platform) {
        // Please avoid changing the order of the calls below, files will be
        // installed in this order.
        var items = [].concat(
            self.getSourceFiles(platform),
            self.getHeaderFiles(platform),
            self.getResourceFiles(platform),
            self.getFrameworks(platform),
            self.getLibFiles(platform)
        );
        return items;
    }
    ///// End of PluginInfo methods /////

    ///// PluginInfo Constructor logic  /////
    self.filepath = path.join(dirname, 'plugin.xml');

    self.dir = dirname;
    var et = self._et = xml_helpers.parseElementtreeSync(self.filepath);
    var pelem = et.getroot();
    self.id = pelem.attrib.id;
    self.version = pelem.attrib.version;

    // Optional fields
    self.name = pelem.findtext('name');
    self.description = pelem.findtext('description');
    self.license = pelem.findtext('license');
    self.repo = pelem.findtext('repo');
    self.issue = pelem.findtext('issue');
    self.keywords = pelem.findtext('keywords');
    self.info = pelem.findtext('info');
    if (self.keywords) {
        self.keywords = self.keywords.split(',').map( function(s) { return s.trim(); } );
    }
    self.getKeywordsAndPlatforms = function () {
        var ret = self.keywords || [];
        return ret.concat('ecosystem:cordova').concat(addCordova(self.getPlatformsArray()));
    };
}  // End of PluginInfo constructor.

// Helper function used to prefix every element of an array with cordova-
// Useful when we want to modify platforms to be cordova-platform
function addCordova(someArray) {
    var newArray = someArray.map(function(element) {
        return 'cordova-' + element;
    });
    return newArray;
}

// Helper function used by most of the getSomething methods of PluginInfo.
// Get all elements of a given name. Both in root and in platform sections
// for the given platform. If transform is given and is a function, it is
// applied to each element.
function _getTags(pelem, tag, platform, transform) {
    var platformTag = pelem.find('./platform[@name="' + platform + '"]');
    if (platform == 'windows' && !platformTag) {
        platformTag = pelem.find('platform[@name="' + 'windows8' + '"]');
    }
    var tagsInRoot = pelem.findall(tag);
    tagsInRoot = tagsInRoot || [];
    var tagsInPlatform = platformTag ? platformTag.findall(tag) : [];
    var tags = tagsInRoot.concat(tagsInPlatform);
    if ( typeof transform === 'function' ) {
        tags = tags.map(transform);
    }
    return tags;
}

// Same as _getTags() but only looks inside a platfrom section.
function _getTagsInPlatform(pelem, tag, platform, transform) {
    var platformTag = pelem.find('./platform[@name="' + platform + '"]');
    if (platform == 'windows' && !platformTag) {
        platformTag = pelem.find('platform[@name="' + 'windows8' + '"]');
    }
    var tags = platformTag ? platformTag.findall(tag) : [];
    if ( typeof transform === 'function' ) {
        tags = tags.map(transform);
    }
    return tags;
}

// Check if x is a string 'true'.
function isStrTrue(x) {
    return String(x).toLowerCase() == 'true';
}

module.exports = PluginInfo;
// Backwards compat:
PluginInfo.PluginInfo = PluginInfo;
PluginInfo.loadPluginsDir = function(dir) {
    var PluginInfoProvider = require('./PluginInfoProvider');
    return new PluginInfoProvider().getAllWithinSearchPath(dir);
};
<|endoftext|>


<commit_before>'use strict';

/**
 * Controller for each tab
 */
define([
	'objects/song',
	'pipeline/pipeline',
	'services/lastfm',
	'pageAction',
	'timer',
	'notifications',
	'services/background-ga'
], function(Song, Pipeline, LastFM, PageAction, Timer, Notifications, GA) {

	/**
	 * Constructor
	 *
	 * @param {Number} tabId
	 * @param {Object} connector
	 */
	return function(tabId, connector) {

		/**
		 * Number of seconds of playback that is considered to be beginning of the track.
		 * Used for re-play detection.
		 */
		var DEFAULT_START_TIME = 10;

		var pageAction = new PageAction(tabId),
			playbackTimer = new Timer(),
			currentSong = null;

		/**
		 * React on state change
		 * @param {Object} newState
		 */
		this.onStateChanged = function(newState) {

			// empty state has same semantics as reset; even if isPlaying, we don't have enough data to use
			var isEmptyState = (!(newState.artist && newState.track) && !newState.uniqueID && !newState.duration);

			if (isEmptyState) {
				// throw away last song and reset state
				if (currentSong !== null) {
					console.log('Tab ' + tabId + ': received empty state - resetting');
					resetState();
				}

				// warning for connector developer
				if (newState.isPlaying) {
					console.log('Tab ' + tabId + ': state from connector doesn\'t contain enough information about the playing track: ' + JSON.stringify(newState));
				}

				return;
			}

			//
			// from here on there is at least some song data
			//

			var hasSongChanged = (currentSong === null || newState.artist !== currentSong.parsed.artist || newState.track !== currentSong.parsed.track ||
															newState.album !== currentSong.parsed.album || newState.uniqueID !== currentSong.parsed.uniqueID);

			// flag for current time of song being on its start;
			// uses 5% of song duration if available or fixed interval
			var isNewStateNearStart = (newState.duration !== null && newState.currentTime <= (newState.duration * 0.05)) ||
										(newState.duration === null && newState.currentTime <= DEFAULT_START_TIME);

			// seeking from song's end to its start is treated as a new play;
			// this also covers automatic replaying of the same song over and over
			var isReplayingSong = (!hasSongChanged && currentSong.isNearEnd() && isNewStateNearStart);

			// propagate values that can change without changing the song
			if (!hasSongChanged && !isReplayingSong) {
				// logging same message over and over saves space in console
				if (newState.isPlaying == currentSong.parsed.isPlaying) {
					console.log('Tab ' + tabId + ': state update: only currentTime has changed');
				} else {
					console.log('Tab ' + tabId + ': state update: ' + JSON.stringify(newState));
				}

				currentSong.parsed.attr({
					currentTime: newState.currentTime,
					isPlaying: newState.isPlaying
				});
			}
			// we've hit a new song (or replaying the previous one) - clear old data and run processing
			else {
				console.log('Tab ' + tabId + ': new track state: ' + JSON.stringify(newState));

				// clear any previous song and its bindings
				resetState();

				currentSong = new Song(newState);
				bindSongListeners(currentSong);

				console.log('Tab ' + tabId + ': new ' + (isReplayingSong ? '(replaying) ' : '') + 'song detected: ' + JSON.stringify(currentSong.attr()));

				// start the timer, actual time will be set after processing is done;
				// we can call doScrobble directly, because the timer will be allowed to trigger only after the song is validated
				playbackTimer.start(function() {
					doScrobble(currentSong);
				});

				// if we just detected the track and it's not playing yet, pause the timer right away;
				// this is important, because isPlaying flag binding only calls pause/resume which assumes the timer is started
				if (!newState.isPlaying) {
					playbackTimer.pause();
				}

				// start processing - result will trigger the listener
				pageAction.setSongLoading(currentSong);
				Pipeline.processSong(currentSong);
			}
		};

		/**
		 * Setup listeners for new song object
		 * @param {Song} song
		 */
		function bindSongListeners(song) {
			/**
			 * Respond to changes of not/playing and pause timer accordingly to get real elapsed time
			 */
			song.bind('parsed.isPlaying', function(ev, newVal) {
				console.log('Tab ' + tabId + ': isPlaying state changed to ' + newVal);

				if (newVal) {
					playbackTimer.resume();

					// maybe the song was not marked as playing yet
					if (!song.flags.isMarkedAsPlaying && (song.flags.isLastfmValid === true || song.flags.isCorrectedByUser === true)) {
						setSongNowPlaying(song);
					}
				} else {
					playbackTimer.pause();
				}
			});

			/**
			 * Song has gone through processing pipeline
			 * This event may occur repeatedly, e.g. when triggered on page load and then corrected by user input
			 */
			song.bind('flags.isProcessed', function(ev, newVal) {
				if (newVal) {
					console.log('Tab ' + tabId + ': song finished processing ', JSON.stringify(song.attr()));
					onProcessed(song);
				} else {
					console.log('Tab ' + tabId + ': song un-processed ', JSON.stringify(song.attr()));
					onUnProcessed(song);
				}
			});
		}

		/**
		 * Unbind all song listener. The song will no longer be used in Controller, but may
		 * remain in async calls and we don't want it to trigger any more listeners.
		 * @param {Song} song
		 */
		function unbindSongListeners(song) {
			song.unbind('parsed.isPlaying');
			song.unbind('flags.isProcessed');
		}

		/**
		 * Resets controller state
		 */
		function resetState() {
			pageAction.setSiteSupported();
			playbackTimer.reset();

			if (currentSong !== null) {
				unbindSongListeners(currentSong);

				// remove notification if song was not scrobbled
				if (!currentSong.flags.isScrobbled) {
					Notifications.remove(currentSong.metadata.notificationId);
				}
			}
			currentSong = null;
		}

		/**
		 * Called when song finishes processing in pipeline. It may not have passed the pipeline
		 * successfully, so checks for various flags are needed.
		 * @param {Song} song
		 */
		function onProcessed(song) {
			// song is considered valid if either L.FM or the user validated it
			if (song.flags.isLastfmValid === true || song.flags.isCorrectedByUser === true) {
				// processing cleans this flag
				song.flags.attr('isMarkedAsPlaying', false);

				// set time-to-scrobble
				playbackTimer.update(song.getSecondsToScrobble());
				console.log('Tab ' + tabId + ': the song will be scrobbled after ' + playbackTimer.getRemainingSeconds() + ' more seconds of playback');

				// if the song is playing, mark it immediately; otherwise will be flagged in isPlaying binding
				if (song.parsed.isPlaying) {
					setSongNowPlaying(song);
				} else {
					pageAction.setSiteSupported();
				}
			} else {
				pageAction.setSongNotRecognized();
			}
		}

		/**
		 * Called when song was already flagged as processed, but now is entering the pipeline again
		 * @param {Song} song
		 */
		function onUnProcessed(song) {
			/* jshint unused: false */
			console.log('Tab ' + tabId + ': clearing playback timer destination time');
			playbackTimer.update(null);
		}

		/**
		 * Contains all actions to be done when song is ready to be marked as now playing
		 * @param {Song} song
		 */
		function setSongNowPlaying(song) {
			pageAction.setSongRecognized(song);

			Notifications.showPlaying(song);

			// send to L.FM
			var nowPlayingCB = function(success) {
				console.log('Tab ' + tabId + ': song set as now playing: ' + success);
			};
			LastFM.sendNowPlaying(song, nowPlayingCB);

			song.flags.attr('isMarkedAsPlaying', true);
		}

		/**
		 * Called when scrobble timer triggers.
		 * The time should be set only after the song is validated and ready to be scrobbled.
		 */
		function doScrobble(song) {
			console.log('Tab ' + tabId + ': scrobbling song ' + song.getArtist() + ' - ' + song.getTrack());

			var scrobbleCB = function(result) {
				if (result.isOk()) {
					console.info('Tab ' + tabId + ': scrobbled successfully');

					song.flags.attr('isScrobbled', true);
					pageAction.setSongScrobbled(song);

					GA.event('core', 'scrobble', connector.label);
				} else {
					console.error('Tab ' + tabId + ': scrobbling failed');
				}
			};

			LastFM.scrobble(song, scrobbleCB);
		}

		/**
		 * Forward event to PageAction
		 */
		this.onPageActionClicked = function() {
			pageAction.onClicked();
		};

		/**
		 * Returns current song as plain object (not can.Map)
		 * @return {{}}
		 */
		this.getCurrentSong = function() {
			return currentSong === null ? {} : currentSong.attr();
		};

		/**
		 * Sets data for current song from user input
		 * TODO: check if all is ok for case when song is already valid
		 */
		this.setUserSongData = function(data) {
			if (currentSong !== null) {
				if (currentSong.flags.isScrobbled) {
					// should not happen
					console.error('Tab ' + tabId + ': attempted to enter user data for already scrobbled song');
					return;
				}

				if (data.artist) {
					currentSong.metadata.attr('userArtist', data.artist);
				}
				if (data.track) {
					currentSong.metadata.attr('userTrack', data.track);
				}

				// re-send song to pipeline
				if (data.artist || data.track) {
					pageAction.setSongLoading(currentSong);
					Pipeline.processSong(currentSong);
				}
			}
		};

		this.toggleLove = function(data, cb) {
			if (currentSong !== null) {
				LastFM.toggleLove(currentSong, data.shouldBeLoved, function() {
					currentSong.metadata.attr('userloved', data.shouldBeLoved);
					cb();
				});
			}
		};

		//
		//
		// Active calls
		//
		//

		// setup initial page action; the controller means the page was recognized
		pageAction.setSiteSupported();

		console.log('Tab ' + tabId + ': created controller for connector: ' + JSON.stringify(connector));
	};

});
<commit_msg>Handle trackart changes<commit_after>'use strict';

/**
 * Controller for each tab
 */
define([
	'objects/song',
	'pipeline/pipeline',
	'services/lastfm',
	'pageAction',
	'timer',
	'notifications',
	'services/background-ga'
], function(Song, Pipeline, LastFM, PageAction, Timer, Notifications, GA) {

	/**
	 * Constructor
	 *
	 * @param {Number} tabId
	 * @param {Object} connector
	 */
	return function(tabId, connector) {

		/**
		 * Number of seconds of playback that is considered to be beginning of the track.
		 * Used for re-play detection.
		 */
		var DEFAULT_START_TIME = 10;

		var pageAction = new PageAction(tabId),
			playbackTimer = new Timer(),
			currentSong = null;

		/**
		 * React on state change
		 * @param {Object} newState
		 */
		this.onStateChanged = function(newState) {

			// empty state has same semantics as reset; even if isPlaying, we don't have enough data to use
			var isEmptyState = (!(newState.artist && newState.track) && !newState.uniqueID && !newState.duration);

			if (isEmptyState) {
				// throw away last song and reset state
				if (currentSong !== null) {
					console.log('Tab ' + tabId + ': received empty state - resetting');
					resetState();
				}

				// warning for connector developer
				if (newState.isPlaying) {
					console.log('Tab ' + tabId + ': state from connector doesn\'t contain enough information about the playing track: ' + JSON.stringify(newState));
				}

				return;
			}

			//
			// from here on there is at least some song data
			//

			var hasSongChanged = (currentSong === null || newState.artist !== currentSong.parsed.artist || newState.track !== currentSong.parsed.track ||
															newState.album !== currentSong.parsed.album || newState.uniqueID !== currentSong.parsed.uniqueID);

			// flag for current time of song being on its start;
			// uses 5% of song duration if available or fixed interval
			var isNewStateNearStart = (newState.duration !== null && newState.currentTime <= (newState.duration * 0.05)) ||
										(newState.duration === null && newState.currentTime <= DEFAULT_START_TIME);

			// seeking from song's end to its start is treated as a new play;
			// this also covers automatic replaying of the same song over and over
			var isReplayingSong = (!hasSongChanged && currentSong.isNearEnd() && isNewStateNearStart);

			// propagate values that can change without changing the song
			if (!hasSongChanged && !isReplayingSong) {
				// logging same message over and over saves space in console
				if (newState.isPlaying == currentSong.parsed.isPlaying) {
					console.log('Tab ' + tabId + ': state update: only currentTime has changed');
				} else {
					console.log('Tab ' + tabId + ': state update: ' + JSON.stringify(newState));
				}

				currentSong.parsed.attr({
					currentTime: newState.currentTime,
					isPlaying: newState.isPlaying,
					trackArt: newState.trackArt,
				});
			}
			// we've hit a new song (or replaying the previous one) - clear old data and run processing
			else {
				console.log('Tab ' + tabId + ': new track state: ' + JSON.stringify(newState));

				// clear any previous song and its bindings
				resetState();

				currentSong = new Song(newState);
				bindSongListeners(currentSong);

				console.log('Tab ' + tabId + ': new ' + (isReplayingSong ? '(replaying) ' : '') + 'song detected: ' + JSON.stringify(currentSong.attr()));

				// start the timer, actual time will be set after processing is done;
				// we can call doScrobble directly, because the timer will be allowed to trigger only after the song is validated
				playbackTimer.start(function() {
					doScrobble(currentSong);
				});

				// if we just detected the track and it's not playing yet, pause the timer right away;
				// this is important, because isPlaying flag binding only calls pause/resume which assumes the timer is started
				if (!newState.isPlaying) {
					playbackTimer.pause();
				}

				// start processing - result will trigger the listener
				pageAction.setSongLoading(currentSong);
				Pipeline.processSong(currentSong);
			}
		};

		/**
		 * Setup listeners for new song object
		 * @param {Song} song
		 */
		function bindSongListeners(song) {
			/**
			 * Respond to changes of not/playing and pause timer accordingly to get real elapsed time
			 */
			song.bind('parsed.isPlaying', function(ev, newVal) {
				console.log('Tab ' + tabId + ': isPlaying state changed to ' + newVal);

				if (newVal) {
					playbackTimer.resume();

					// maybe the song was not marked as playing yet
					if (!song.flags.isMarkedAsPlaying && (song.flags.isLastfmValid === true || song.flags.isCorrectedByUser === true)) {
						setSongNowPlaying(song);
					}
				} else {
					playbackTimer.pause();
				}
			});

			/**
			 * Song has gone through processing pipeline
			 * This event may occur repeatedly, e.g. when triggered on page load and then corrected by user input
			 */
			song.bind('flags.isProcessed', function(ev, newVal) {
				if (newVal) {
					console.log('Tab ' + tabId + ': song finished processing ', JSON.stringify(song.attr()));
					onProcessed(song);
				} else {
					console.log('Tab ' + tabId + ': song un-processed ', JSON.stringify(song.attr()));
					onUnProcessed(song);
				}
			});
		}

		/**
		 * Unbind all song listener. The song will no longer be used in Controller, but may
		 * remain in async calls and we don't want it to trigger any more listeners.
		 * @param {Song} song
		 */
		function unbindSongListeners(song) {
			song.unbind('parsed.isPlaying');
			song.unbind('flags.isProcessed');
		}

		/**
		 * Resets controller state
		 */
		function resetState() {
			pageAction.setSiteSupported();
			playbackTimer.reset();

			if (currentSong !== null) {
				unbindSongListeners(currentSong);

				// remove notification if song was not scrobbled
				if (!currentSong.flags.isScrobbled) {
					Notifications.remove(currentSong.metadata.notificationId);
				}
			}
			currentSong = null;
		}

		/**
		 * Called when song finishes processing in pipeline. It may not have passed the pipeline
		 * successfully, so checks for various flags are needed.
		 * @param {Song} song
		 */
		function onProcessed(song) {
			// song is considered valid if either L.FM or the user validated it
			if (song.flags.isLastfmValid === true || song.flags.isCorrectedByUser === true) {
				// processing cleans this flag
				song.flags.attr('isMarkedAsPlaying', false);

				// set time-to-scrobble
				playbackTimer.update(song.getSecondsToScrobble());
				console.log('Tab ' + tabId + ': the song will be scrobbled after ' + playbackTimer.getRemainingSeconds() + ' more seconds of playback');

				// if the song is playing, mark it immediately; otherwise will be flagged in isPlaying binding
				if (song.parsed.isPlaying) {
					setSongNowPlaying(song);
				} else {
					pageAction.setSiteSupported();
				}
			} else {
				pageAction.setSongNotRecognized();
			}
		}

		/**
		 * Called when song was already flagged as processed, but now is entering the pipeline again
		 * @param {Song} song
		 */
		function onUnProcessed(song) {
			/* jshint unused: false */
			console.log('Tab ' + tabId + ': clearing playback timer destination time');
			playbackTimer.update(null);
		}

		/**
		 * Contains all actions to be done when song is ready to be marked as now playing
		 * @param {Song} song
		 */
		function setSongNowPlaying(song) {
			pageAction.setSongRecognized(song);

			Notifications.showPlaying(song);

			// send to L.FM
			var nowPlayingCB = function(success) {
				console.log('Tab ' + tabId + ': song set as now playing: ' + success);
			};
			LastFM.sendNowPlaying(song, nowPlayingCB);

			song.flags.attr('isMarkedAsPlaying', true);
		}

		/**
		 * Called when scrobble timer triggers.
		 * The time should be set only after the song is validated and ready to be scrobbled.
		 */
		function doScrobble(song) {
			console.log('Tab ' + tabId + ': scrobbling song ' + song.getArtist() + ' - ' + song.getTrack());

			var scrobbleCB = function(result) {
				if (result.isOk()) {
					console.info('Tab ' + tabId + ': scrobbled successfully');

					song.flags.attr('isScrobbled', true);
					pageAction.setSongScrobbled(song);

					GA.event('core', 'scrobble', connector.label);
				} else {
					console.error('Tab ' + tabId + ': scrobbling failed');
				}
			};

			LastFM.scrobble(song, scrobbleCB);
		}

		/**
		 * Forward event to PageAction
		 */
		this.onPageActionClicked = function() {
			pageAction.onClicked();
		};

		/**
		 * Returns current song as plain object (not can.Map)
		 * @return {{}}
		 */
		this.getCurrentSong = function() {
			return currentSong === null ? {} : currentSong.attr();
		};

		/**
		 * Sets data for current song from user input
		 * TODO: check if all is ok for case when song is already valid
		 */
		this.setUserSongData = function(data) {
			if (currentSong !== null) {
				if (currentSong.flags.isScrobbled) {
					// should not happen
					console.error('Tab ' + tabId + ': attempted to enter user data for already scrobbled song');
					return;
				}

				if (data.artist) {
					currentSong.metadata.attr('userArtist', data.artist);
				}
				if (data.track) {
					currentSong.metadata.attr('userTrack', data.track);
				}

				// re-send song to pipeline
				if (data.artist || data.track) {
					pageAction.setSongLoading(currentSong);
					Pipeline.processSong(currentSong);
				}
			}
		};

		this.toggleLove = function(data, cb) {
			if (currentSong !== null) {
				LastFM.toggleLove(currentSong, data.shouldBeLoved, function() {
					currentSong.metadata.attr('userloved', data.shouldBeLoved);
					cb();
				});
			}
		};

		//
		//
		// Active calls
		//
		//

		// setup initial page action; the controller means the page was recognized
		pageAction.setSiteSupported();

		console.log('Tab ' + tabId + ': created controller for connector: ' + JSON.stringify(connector));
	};

});
<|endoftext|>


<commit_before>/* global jQuery */

; (function ($, undefined) {

    // Plugin Logic
    $.bootstrapGrowl = function (message, options) {
        // Set our options from the defaults, overriding with the
        // parameter we pass into this function
        options = $.extend({}, $.bootstrapGrowl.options, options);

        // Create a div element
        var $alert = $('
');

        $alert.addClass('bootstrap-growl alert');

        // If the type if set, then add the alert-* class name
        if (options.type) {
            $alert.addClass('alert-' + options.type);
        }

        // If the allow dismissal is set, the add the relevant class
        if (options.allow_dismiss) {
            $alert.addClass('alert-dismissible');
            $alert.append('<button  class="close" data-dismiss="alert" type="button">×Close</button>');
        }

        // Append the message
        $alert.append(message);

        if (options.top_offset) {
            options.offset = {
                from: 'top',
                amount: options.top_offset
            };
        }

        // Cache the jQuery object selector
        var $this = null,
            offsetAmount = options.offset.amount;

        $('.bootstrap-growl').each(function () {
            $this = $(this);
            offsetAmount = Math.max(offsetAmount, parseInt($this.css(options.offset.from)) + $this.outerHeight() + options.stackup_spacing);
        });

        var css = {
            'position': (options.element === 'body' ? 'fixed' : 'absolute'),
            'margin': 0,
            'z-index': '9999',
            'display': 'none'
        };

        css[options.offset.from] = offsetAmount + 'px';

        if (options.width !== 'auto') {
           css['width'] = options.width + 'px';
        }

        // Apply the css styles
        $alert.css(css);

        // Append the alert to the parent element
        $(options.element).append($alert);

        switch (options.align) {
            case 'center':
                $alert.css({
                    'left': '50%',
                    'margin-left': '-' + ($alert.outerWidth() / 2) + 'px'
                });
                break;

            case 'left':
                $alert.css('left', '20px');
                break;

            default:
                $alert.css('right', '20px');
        }

        $alert.fadeIn();

        if (options.delay > 0) {
            $alert.delay(options.delay).fadeOut(function () {
                return $(this).alert('close');
            });
        }

        return $alert;
    };

    // Methods (Private)

    // Defaults
    $.bootstrapGrowl.options = {
        element: 'body',
        type: 'info',
        offset: {
            from: 'top',
            amount: 20
        },
        align: 'right',
        width: 250,
        delay: 4000,
        allow_dismiss: true,
        stackup_spacing: 10
    };

})(jQuery);
<commit_msg>Updated comments<commit_after>/* global jQuery */

; (function ($, undefined) {

    // Plugin Logic

    $.bootstrapGrowl = function (message, options) {
        // Set our options from the defaults, overriding with the
        // parameter we pass into this function
        options = $.extend({}, $.bootstrapGrowl.options, options);

        // Create a div element
        var $alert = $('
');

        // Add the following classes
        $alert.addClass('bootstrap-growl alert');

        // If the 'type' if set, then add the alert-* class name
        if (options.type) {
            $alert.addClass('alert-' + options.type);
        }

        // If the 'allow dismissal' is set, then add the relevant class and append a button element
        if (options.allow_dismiss) {
            $alert.addClass('alert-dismissible');
            $alert.append('<button  class="close" data-dismiss="alert" type="button">×Close</button>');
        }

        // Append the message to the alert
        $alert.append(message);

        // If the 'top offset' is set, then create an offset object literal
        if (options.top_offset) {
            options.offset = {
                from: 'top',
                amount: options.top_offset
            };
        }

        // Cache the jQuery object selector
        var $this = null,

            // Store the offset amount
            offsetAmount = options.offset.amount;

        // For each element with the class name of '.bootstrap-growl', calculate the offset
        $('.bootstrap-growl').each(function () {
            $this = $(this);
            offsetAmount = Math.max(offsetAmount, parseInt($this.css(options.offset.from)) + $this.outerHeight() + options.stackup_spacing);
        });

        // Create a css object literal
        var css = {
            'position': (options.element === 'body' ? 'fixed' : 'absolute'),
            'margin': 0,
            'z-index': '9999',
            'display': 'none'
        };

        css[options.offset.from] = offsetAmount + 'px';

        if (options.width !== 'auto') {
           css['width'] = options.width + 'px';
        }

        // Apply the css styles from above
        $alert.css(css);

        // Append the alert to the parent element
        $(options.element).append($alert);

        // Apply the css styles with alignment
        switch (options.align) {
            case 'center':
                $alert.css({
                    'left': '50%',
                    'margin-left': '-' + ($alert.outerWidth() / 2) + 'px'
                });
                break;

            case 'left':
                $alert.css('left', '20px');
                break;

            default:
                $alert.css('right', '20px');
        }

        // Display the alert by fading in
        $alert.fadeIn();

        // Create a delay on fade out
        if (options.delay > 0) {
            $alert.delay(options.delay).fadeOut(function () {
                return $(this).alert('close');
            });
        }

        return $alert;
    };

    // Methods (Private)

    // Defaults
    $.bootstrapGrowl.options = {
        element: 'body',
        type: 'info',
        offset: {
            from: 'top',
            amount: 20
        },
        align: 'right',
        width: 250,
        delay: 4000,
        allow_dismiss: true,
        stackup_spacing: 10
    };

})(jQuery);
<|endoftext|>


<commit_before>/**
* jQuery Youtube API Feed Plugin
* @author John Hoover <john@defvayne23.com>
* @version 1.3.0
*/
(function($){$.extend({jTube:function(g){var g=$.extend({user:'',userType:'uploads',search:'',feed:'',playlist:'',format:'flash',order:'published',time:'all_time',limit:5,page:1,success:function(a,b){},error:function(a){}},g);var h='http://gdata.youtube.com/feeds/';var i=this;var j='';if(g.user!='')h+='users/'+g.user+'/'+g.userType+'?';else if(g.search!='')h+='api/videos?q='+g.search+'&';else if(g.feed!='')h+='api/standardfeeds/'+g.feed+'?';else if(g.playlist!='')h+='api/playlists/'+g.playlist+'?';else{g.error("No feed choices given.");return false}h+='alt=json';h+='&max-results='+g.limit;h+='&start-index='+(((g.page*g.limit)-g.limit)+1);h+='&orderby='+g.order;h+='&time='+g.time;if(g.format=="mpeg")h+='&format=6';else if(g.format=="h263")h+='&format=1';else h+='&format=5';$.ajax({url:h,dataType:'json',success:function(d){if(d!=null){if(g.user!=''&&g.userType=='playlists'){var e=[];$(d.feed.entry).each(function(){var a={title:this.title.$t,description:this.yt$description.$t,id:this.yt$playlistId.$t,link:this.link[1].href,published:jTubePublished(this.published.$t)};e[e.length]=a});var f=e}else{var f=[];$(d.feed.entry).each(function(){var b=[];$(this.category).each(function(a){if(a!=0){b[a-1]=this.term}});var c={title:this.title.$t,description:this.media$group.media$description.$t,link:this.link[0].href,categories:b,author:{name:this.author[0].name.$t,link:this.author[0].uri.$t},thumbnail:this.media$group.media$thumbnail[3].url};videoFormats=[];$(this.media$group.media$content).each(function(){videoFormats[this.yt$format]=this.url});if(g.format=="mpeg")c.video=videoFormats[6];else if(g.format=="h263")c.video=videoFormats[1];else c.video=videoFormats[5];if(this.published)c.published=jTubePublished(this.published.$t);if(this.media$group.yt$duration.seconds){duration=this.media$group.yt$duration.seconds;hours=0;minutes=0;seconds=0;while(duration>=3600){hours=hours+1;duration=duration-3600}while(duration>=60){minutes=minutes+1;duration=duration-60}seconds=duration;if(seconds<10)seconds='0'+seconds;c.length=minutes+':'+seconds;if(hours>0)c.length=hours+':'+c.length}if(this.yt$statistics)c.views=this.yt$statistics.viewCount;f[f.length]=c})}pages=Math.ceil(d.feed.openSearch$totalResults.$t/g.limit);g.success(f,pages)}else{g.error("Bad request.")}}});return this},jTubeEmbed:function(a,b){var b=$.extend({width:290,height:250,autoplay:true,fullscreeen:false,related:true,loop:false,keyboard:true,genie:false,border:false,highdef:true,start:0},b);var c=a+"?";c+='autoplay='+(b.autoPlay?1:0);c+='&fs='+(b.fullscreen?1:0);c+='&rl=1'+(b.related?1:0);c+='&loop=1'+(b.loop?1:0);c+='&disablekb=0'+(b.keyboard?0:1);c+='&egm=1'+(b.genie?1:0);c+='&border=1'+(b.border?1:0);c+='&hd=1'+(b.highdef?1:0);c+='&start='+b.start;var d='<object width="'+b.width+'" height="'+b.height+'">';d+='<param name="movie" value="'+c+'"</param>';d+='<param name="allowScriptAccess" value="always"></param>';if(b.fullscreen==true)d+='<param name="allowFullScreen" value="true"></param>';d+='<embed src="'+c+'"';d+='    type="application/x-shockwave-flash"';if(b.fullscreen==true)d+='    allowfullscreen="true"';d+='    allowscriptaccess="always"';d+='    width="'+b.width+'" height="'+b.height+'">';d+='    </embed>';d+='</object>';return d},})})(jQuery);function jTubePublished(a){a=a.match(/([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/);meridiem="pm";hour=a[4];if(hour<12){meridiem="am"}else{if(hour>12)hour=hour-12}return{year:a[1],month:a[2],day:a[3],hour:hour,minute:a[5],seconds:a[6],meridiem:meridiem}}<commit_msg>Change the version number in the min file.<commit_after>/**
* jQuery Youtube API Feed Plugin
* @author John Hoover <john@defvayne23.com>
* @version 1.3.1
*/
(function($){$.extend({jTube:function(g){var g=$.extend({user:'',userType:'uploads',search:'',feed:'',playlist:'',format:'flash',order:'published',time:'all_time',limit:5,page:1,success:function(a,b){},error:function(a){}},g);var h='http://gdata.youtube.com/feeds/';var i=this;var j='';if(g.user!='')h+='users/'+g.user+'/'+g.userType+'?';else if(g.search!='')h+='api/videos?q='+g.search+'&';else if(g.feed!='')h+='api/standardfeeds/'+g.feed+'?';else if(g.playlist!='')h+='api/playlists/'+g.playlist+'?';else{g.error("No feed choices given.");return false}h+='alt=json';h+='&max-results='+g.limit;h+='&start-index='+(((g.page*g.limit)-g.limit)+1);h+='&orderby='+g.order;h+='&time='+g.time;if(g.format=="mpeg")h+='&format=6';else if(g.format=="h263")h+='&format=1';else h+='&format=5';$.ajax({url:h,dataType:'json',success:function(d){if(d!=null){if(g.user!=''&&g.userType=='playlists'){var e=[];$(d.feed.entry).each(function(){var a={title:this.title.$t,description:this.yt$description.$t,id:this.yt$playlistId.$t,link:this.link[1].href,published:jTubePublished(this.published.$t)};e[e.length]=a});var f=e}else{var f=[];$(d.feed.entry).each(function(){var b=[];$(this.category).each(function(a){if(a!=0){b[a-1]=this.term}});var c={title:this.title.$t,description:this.media$group.media$description.$t,link:this.link[0].href,categories:b,author:{name:this.author[0].name.$t,link:this.author[0].uri.$t},thumbnail:this.media$group.media$thumbnail[3].url};videoFormats=[];$(this.media$group.media$content).each(function(){videoFormats[this.yt$format]=this.url});if(g.format=="mpeg")c.video=videoFormats[6];else if(g.format=="h263")c.video=videoFormats[1];else c.video=videoFormats[5];if(this.published)c.published=jTubePublished(this.published.$t);if(this.media$group.yt$duration.seconds){duration=this.media$group.yt$duration.seconds;hours=0;minutes=0;seconds=0;while(duration>=3600){hours=hours+1;duration=duration-3600}while(duration>=60){minutes=minutes+1;duration=duration-60}seconds=duration;if(seconds<10)seconds='0'+seconds;c.length=minutes+':'+seconds;if(hours>0)c.length=hours+':'+c.length}if(this.yt$statistics)c.views=this.yt$statistics.viewCount;f[f.length]=c})}pages=Math.ceil(d.feed.openSearch$totalResults.$t/g.limit);g.success(f,pages)}else{g.error("Bad request.")}}});return this},jTubeEmbed:function(a,b){var b=$.extend({width:290,height:250,autoplay:true,fullscreeen:false,related:true,loop:false,keyboard:true,genie:false,border:false,highdef:true,start:0},b);var c=a+"?";c+='autoplay='+(b.autoPlay?1:0);c+='&fs='+(b.fullscreen?1:0);c+='&rl=1'+(b.related?1:0);c+='&loop=1'+(b.loop?1:0);c+='&disablekb=0'+(b.keyboard?0:1);c+='&egm=1'+(b.genie?1:0);c+='&border=1'+(b.border?1:0);c+='&hd=1'+(b.highdef?1:0);c+='&start='+b.start;var d='<object width="'+b.width+'" height="'+b.height+'">';d+='<param name="movie" value="'+c+'"</param>';d+='<param name="allowScriptAccess" value="always"></param>';if(b.fullscreen==true)d+='<param name="allowFullScreen" value="true"></param>';d+='<embed src="'+c+'"';d+='    type="application/x-shockwave-flash"';if(b.fullscreen==true)d+='    allowfullscreen="true"';d+='    allowscriptaccess="always"';d+='    width="'+b.width+'" height="'+b.height+'">';d+='    </embed>';d+='</object>';return d},})})(jQuery);function jTubePublished(a){a=a.match(/([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/);meridiem="pm";hour=a[4];if(hour<12){meridiem="am"}else{if(hour>12)hour=hour-12}return{year:a[1],month:a[2],day:a[3],hour:hour,minute:a[5],seconds:a[6],meridiem:meridiem}}<|endoftext|>


<commit_before>/**
 *
 *  Matrix Group Branding Area jQuery Plugin
 *  Author: Roger Vandawalker <rvandawalker@matrixgroup.net>, @rjv
 *  Version: 1.1.2
 *
 *  Dependencies: jQuery 1.4+ (http://jquery.com)
 *  Supports: IE7+, Chrome, Safari, Firefox 3.6+
 *
 *  Copyright (c) 2012 Matrix Group International <http://matrixgroup.net>
 *  Released under the MIT license
 *  http://www.opensource.org/licenses/mit-license.php
 *
 */

;(function( $, window, document, undefined ) {
  var Branding = {

    _classes: {
      slide: 'slide',
      element: 'mtx-branding',
      container: 'slides-container',
      indicators: 'slide-indicators',
      background: 'slide-background',
      thumbnails: 'slide-thumbnails',
      thumbnail: 'slide-thumbnail'
    },

    _init: function( options, elem ) {
      var self = this;

      self.elem = elem;
      self.$elem = $(elem);

      self.options = $.extend( {}, $.fn.matrixBranding.options, options );

      self.$elem.addClass(self._classes.element);
      self.wrapSlides();

      if ( self.getSlideCount() > 1 ) { // only append slide navigation if there is more than one slide
        self.appendSlideNavigation();
      }

      if ( self.options.showSlideIndicators && self.getSlideCount() > 1 ) { // only append slide
        self.appendSlideIndicators();
      }

      if ( self.options.showSlideThumbnails ) {
        self.appendThumbnails();
      }

      self.selectInitialSlide();

      if ( self.options.autoPlay ) {
        self.showTimer = setTimeout(function() {
          self.playSlideshow();
        }, self.options.slideDuration);
      }
    },

    wrapSlides: function() {
      var self = this;

      self.$elem.children().addClass(self._classes.slide).wrapAll($('<div/>', { "class": self._classes.container }));
    },

    selectInitialSlide: function() {
      var self = this,
          slideNumber = self.options.initialSlide - 1;

      if ( self.options.startWithRandomSlide ) {
        slideNumber = Math.floor(Math.random() * self.getSlideCount());
      }

      self.showSlide(self.$elem.find("."+self._classes.slide).eq(slideNumber));
    },

    appendSlideNavigation: function() {
      var self = this,
          $apn = self.options.assignPreviousNavigation,
          $ann = self.options.assignNextNavigation;

      // assign or create the "previous slide" navigation button

      // if the previous button has been explicitly assigned with a jquery object,
      // add the "previous" click event to it
      if ( $apn instanceof jQuery ) {
        $apn.bind("click",function(e) {
          e.preventDefault();
          if (self.options.pauseOnClick) {
            self.pauseSlideshow();
          }
          self.showPreviousSlide();
        });
      } else {
        $("<a/>", {
          href: 'javascript:void(0);',
          html: self.options.previousButtonText,
          title: "Previous Slide",
          "class": self.options.previousButtonClass,
          click: function(e) {
            e.preventDefault();
            if (self.options.pauseOnClick) {
              self.pauseSlideshow();
            }
            self.showPreviousSlide();
          }
        }).appendTo(self.$elem);
      }

      // if the next button has been explicitly assigned with a jquery object,
      // add the "next" click event to it
      if ( $ann instanceof jQuery ) {
        $ann.bind("click",function(e) {
          e.preventDefault();
          if (self.options.pauseOnClick) {
            self.pauseSlideshow();
          }
          self.showNextSlide();
        });
      } else {
        $("<a/>", {
          href: 'javascript:void(0);',
          html: self.options.nextButtonText,
          title: "Next Slide",
          "class": self.options.nextButtonClass,
          click: function(e) {
            e.preventDefault();
            if (self.options.pauseOnClick) {
              self.pauseSlideshow();
            }
            self.showNextSlide();
          }
        }).appendTo(self.$elem);
      }
    },

    appendSlideIndicators: function() {
      var self = this,
          $indicators = $('<ul/>', { "class": self._classes.indicators }),
          $slides = self.$elem.find("."+self._classes.slide);

      $slides.each( function(i) {
        $('<a>',
          {
            text: (i+1),
            href: 'javascript:void(0);',
            click: function() {
              var $li = $(this).parent();

              if ( self.options.pauseOnClick ) {
                self.pauseSlideshow();
              }
              self.showSlide( $slides.eq($li.index()) );
            }
          }
        ).appendTo($indicators);
      });

      $indicators.children().wrap('<li/>');
      $indicators.appendTo(self.$elem);
    },

    appendThumbnails: function() {
      var self = this,
          $thumbnails = self.options.assignThumbnailsContainer != null
            ? self.options.assignThumbnailsContainer.addClass(self._classes.thumbnails)
            : $('<div/>', { "class": self._classes.thumbnails }),
          $slides = self.$elem.find("."+self._classes.slide);

      // recalculate the container width after all the images have loaded
      $(window).load(function(){
        $(window).trigger("resize");
      }).resize(function(){
        // if ($thumbnails.data('thumbs') !== undefined) { return; }
        $thumbnails.data( "thumbs", { "width": 0, "count": 0 } ); // set the default width to 0
        $thumbnails.find(".thumb").each(function(){

          $thumbnails.data( 'thumbs', {
            "width": $thumbnails.data("thumbs").width + $(this).outerWidth() + Number($(this).css("margin-left").replace("px","")),
            "count": $thumbnails.data("thumbs").count + 1
          });

        });

        var newWidth = Math.ceil($thumbnails.data('thumbs').width) + 1;
        // alert(newWidth);

        $thumbnails.children("div").css({
          // round up the width and add 1px in order to account for improper subpixel rendering in IE<9
          width: newWidth
        });
      });

      $slides.each( function(i) {

        // check to see if the slide has a pre-set thumbnail image
        // if not, use the slide background image

        $theThumbnail = $(this).find("."+self._classes.thumbnail).length === 1
          ? $(this).find("."+self._classes.thumbnail).eq(0)
          : $(this).find("img."+self._classes.background);

        var $newThumb = $("<span/>",
        {
          "class": "thumb",
          click: function() {
            if ( self.options.pauseOnClick ) {
              self.pauseSlideshow();
            }
            self.showSlide( $slides.eq($(this).index()) );
          }
        });

        $theThumbnail.clone().removeClass("slide-background").removeAttr("id").appendTo($newThumb);

        $newThumb.appendTo($thumbnails);

      });

      $thumbnails.children().wrapAll('<div/>');

      if ( self.options.assignThumbnailsContainer === null ){
        $thumbnails.insertAfter(self.$elem);
      }

      self.options.assignThumbnailsContainer = $thumbnails;
    },

    showNextSlide: function() {
      var self = this,
          $slides = self.$elem.find("."+self._classes.slide),
          $current = $slides.filter("."+self.options.selectedSlideClass);

      if ( ($current.index() + 1) % $slides.length === 0 ) {
        // the last slide has been reached; jump to first slide
        self.showSlide($slides.first());
      } else {
        self.showSlide($current.next("."+self._classes.slide));
      }
    },

    showPreviousSlide: function() {
      var self = this,
        $slides = self.$elem.find("."+self._classes.slide),
        $current = $slides.filter("."+self.options.selectedSlideClass);

      if ( $current.index() === 0 ) {
        // we're at the first slide; jump to last slide
        self.showSlide($slides.last());
      } else {
        self.showSlide($current.prev("."+self._classes.slide));
      }
    },

    showSlide: function( $slide ) {
      var self = this,
        index = $slide.index();

      $slide.addClass(self.options.selectedSlideClass).fadeIn(self.options.transitionDuration)
        .siblings().removeClass(self.options.selectedSlideClass).fadeOut(self.options.transitionDuration);

      if ( self.options.showSlideIndicators ) {
        self.showSlideIndicator($slide);
      }

      if ( self.options.showSlideThumbnails ) {
        self.showSlideThumbnail($slide);
      }
    },

    showSlideIndicator: function( $slide ) {
      var self = this,
        index = $slide.index();

      if ( self.options.showSlideIndicators ) {
        self.$elem.find("."+self._classes.indicators).children().eq(index).addClass(self.options.selectedSlideClass)
          .siblings().removeClass(self.options.selectedSlideClass);
      }
    },

    showSlideThumbnail: function ( $slide ) {
      var self = this,
        index = $slide.index(),
        $thumbContainer = self.options.assignThumbnailsContainer, // self.$elem.next("."+self._classes.thumbnails),
        $thumb = $thumbContainer.find(".thumb").eq(index);

      if ( self.options.showSlideThumbnails ) {
        $thumb.addClass(self.options.selectedSlideClass)
          .siblings().removeClass(self.options.selectedSlideClass);

        self.slideToThumbnail( $thumbContainer, $thumb );
      }
    },

    slideToThumbnail: function ( $thumbContainer, $thumb ) {
      var self = this,
        leftScrollTo = -1,
        topScrollTo = -1,
        thumbPos = $thumb.position(),
        thumbWidth = $thumb.outerWidth(),
        thumbHeight = $thumb.outerHeight(),
        containerWidth = $thumbContainer.outerWidth(),
        containerHeight = $thumbContainer.outerHeight();

      if ( ((thumbWidth + thumbPos.left) > (containerWidth + $thumbContainer.scrollLeft()))
           || ($thumbContainer.scrollLeft() > thumbPos.left) ){
        leftScrollTo = thumbPos.left;
      }

      if ( ((thumbHeight + thumbPos.top) > (containerHeight + $thumbContainer.scrollTop()))
           || ($thumbContainer.scrollTop() > thumbPos.top) ){
        topScrollTo = thumbPos.top;
      }

      if ( leftScrollTo > -1 || topScrollTo > -1 ){
        $thumbContainer.animate({
          scrollLeft: leftScrollTo < 0 ? 0 : leftScrollTo,
          scrollTop: topScrollTo < 0 ? 0 : topScrollTo
        }, self.options.transitionDuration);
      }
    },

    playSlideshow: function() {
      var self = this;

      self.showNextSlide();

      self.showTimer = setTimeout(function() {
        self.playSlideshow();
      }, self.options.slideDuration);
    },

    pauseSlideshow: function() {
      var self = this;
      clearTimeout(self.showTimer);
    },

    getSlideCount: function() {
      var self = this,
          $slides = self.$elem.find("."+self._classes.slide);

      return $slides.length;
    }

  };

  $.fn.matrixBranding = function( options ) {
    return this.each(function() {

      var branding = Object.create( Branding );
      branding._init( options, this );

    });
  };

  $.fn.matrixBranding.options = {
    nextButtonClass: 'next',        // class name applied to the "next" button
    previousButtonClass: 'prev',    // class name applied to the "previous" button
    nextButtonText: '→',       // text for the auto-generated "next" button
    previousButtonText: '←',   // text for the auto-generated "previous" button
    selectedSlideClass: 'current',  // class name applied to the visible slide
    transitionDuration: 800,        // number in ms for how long a slide transition should be
    slideDuration: 7000,            // number in ms for how long a slide should show
    autoPlay: true,                 // set to false to prevent an automatic slideshow
    pauseOnClick: true,             // set to false to prevent pausing the slideshow on clicking slide navigation
    showSlideIndicators: false,     // set to true to automatically add a slide navigation list
    showSlideThumbnails: false,
    assignThumbnailsContainer: null,
    initialSlide: 1,                // explictly set a slide to show first
    startWithRandomSlide: false,    // set to true for a random initial slide
    assignNextNavigation: null,       // a jquery object to be binded as slide navigation
    assignPreviousNavigation: null    // a jquery object to be binded as slide navigation
  };
})( jQuery, window, document );

// Polyfill for Object.create method
if ( typeof Object.create !== 'function' ) {
  Object.create = function( obj ) {
    function F() {};
    F.prototype = obj;
    return new F();
  };
}
<commit_msg>Don't autoplay if there is only one slide<commit_after>/**
 *
 *  Matrix Group Branding Area jQuery Plugin
 *  Author: Roger Vandawalker <rvandawalker@matrixgroup.net>, @rjv
 *  Version: 1.1.2
 *
 *  Dependencies: jQuery 1.4+ (http://jquery.com)
 *  Supports: IE7+, Chrome, Safari, Firefox 3.6+
 *
 *  Copyright (c) 2012 Matrix Group International <http://matrixgroup.net>
 *  Released under the MIT license
 *  http://www.opensource.org/licenses/mit-license.php
 *
 */

;(function( $, window, document, undefined ) {
  var Branding = {

    _classes: {
      slide: 'slide',
      element: 'mtx-branding',
      container: 'slides-container',
      indicators: 'slide-indicators',
      background: 'slide-background',
      thumbnails: 'slide-thumbnails',
      thumbnail: 'slide-thumbnail'
    },

    _init: function( options, elem ) {
      var self = this;

      self.elem = elem;
      self.$elem = $(elem);

      self.options = $.extend( {}, $.fn.matrixBranding.options, options );

      self.$elem.addClass(self._classes.element);
      self.wrapSlides();

      if ( self.getSlideCount() > 1 ) { // only append slide navigation if there is more than one slide
        self.appendSlideNavigation();
      }

      if ( self.options.showSlideIndicators && self.getSlideCount() > 1 ) { // only append slide
        self.appendSlideIndicators();
      }

      if ( self.options.showSlideThumbnails ) {
        self.appendThumbnails();
      }

      self.selectInitialSlide();

      if ( self.options.autoPlay && self.getSlideCount() > 1 ) {
        self.showTimer = setTimeout(function() {
          self.playSlideshow();
        }, self.options.slideDuration);
      }
    },

    wrapSlides: function() {
      var self = this;

      self.$elem.children().addClass(self._classes.slide).wrapAll($('<div/>', { "class": self._classes.container }));
    },

    selectInitialSlide: function() {
      var self = this,
          slideNumber = self.options.initialSlide - 1;

      if ( self.options.startWithRandomSlide ) {
        slideNumber = Math.floor(Math.random() * self.getSlideCount());
      }

      self.showSlide(self.$elem.find("."+self._classes.slide).eq(slideNumber));
    },

    appendSlideNavigation: function() {
      var self = this,
          $apn = self.options.assignPreviousNavigation,
          $ann = self.options.assignNextNavigation;

      // assign or create the "previous slide" navigation button

      // if the previous button has been explicitly assigned with a jquery object,
      // add the "previous" click event to it
      if ( $apn instanceof jQuery ) {
        $apn.bind("click",function(e) {
          e.preventDefault();
          if (self.options.pauseOnClick) {
            self.pauseSlideshow();
          }
          self.showPreviousSlide();
        });
      } else {
        $("<a/>", {
          href: 'javascript:void(0);',
          html: self.options.previousButtonText,
          title: "Previous Slide",
          "class": self.options.previousButtonClass,
          click: function(e) {
            e.preventDefault();
            if (self.options.pauseOnClick) {
              self.pauseSlideshow();
            }
            self.showPreviousSlide();
          }
        }).appendTo(self.$elem);
      }

      // if the next button has been explicitly assigned with a jquery object,
      // add the "next" click event to it
      if ( $ann instanceof jQuery ) {
        $ann.bind("click",function(e) {
          e.preventDefault();
          if (self.options.pauseOnClick) {
            self.pauseSlideshow();
          }
          self.showNextSlide();
        });
      } else {
        $("<a/>", {
          href: 'javascript:void(0);',
          html: self.options.nextButtonText,
          title: "Next Slide",
          "class": self.options.nextButtonClass,
          click: function(e) {
            e.preventDefault();
            if (self.options.pauseOnClick) {
              self.pauseSlideshow();
            }
            self.showNextSlide();
          }
        }).appendTo(self.$elem);
      }
    },

    appendSlideIndicators: function() {
      var self = this,
          $indicators = $('<ul/>', { "class": self._classes.indicators }),
          $slides = self.$elem.find("."+self._classes.slide);

      $slides.each( function(i) {
        $('<a>',
          {
            text: (i+1),
            href: 'javascript:void(0);',
            click: function() {
              var $li = $(this).parent();

              if ( self.options.pauseOnClick ) {
                self.pauseSlideshow();
              }
              self.showSlide( $slides.eq($li.index()) );
            }
          }
        ).appendTo($indicators);
      });

      $indicators.children().wrap('<li/>');
      $indicators.appendTo(self.$elem);
    },

    appendThumbnails: function() {
      var self = this,
          $thumbnails = self.options.assignThumbnailsContainer != null
            ? self.options.assignThumbnailsContainer.addClass(self._classes.thumbnails)
            : $('<div/>', { "class": self._classes.thumbnails }),
          $slides = self.$elem.find("."+self._classes.slide);

      // recalculate the container width after all the images have loaded
      $(window).load(function(){
        $(window).trigger("resize");
      }).resize(function(){
        // if ($thumbnails.data('thumbs') !== undefined) { return; }
        $thumbnails.data( "thumbs", { "width": 0, "count": 0 } ); // set the default width to 0
        $thumbnails.find(".thumb").each(function(){

          $thumbnails.data( 'thumbs', {
            "width": $thumbnails.data("thumbs").width + $(this).outerWidth() + Number($(this).css("margin-left").replace("px","")),
            "count": $thumbnails.data("thumbs").count + 1
          });

        });

        var newWidth = Math.ceil($thumbnails.data('thumbs').width) + 1;
        // alert(newWidth);

        $thumbnails.children("div").css({
          // round up the width and add 1px in order to account for improper subpixel rendering in IE<9
          width: newWidth
        });
      });

      $slides.each( function(i) {

        // check to see if the slide has a pre-set thumbnail image
        // if not, use the slide background image

        $theThumbnail = $(this).find("."+self._classes.thumbnail).length === 1
          ? $(this).find("."+self._classes.thumbnail).eq(0)
          : $(this).find("img."+self._classes.background);

        var $newThumb = $("<span/>",
        {
          "class": "thumb",
          click: function() {
            if ( self.options.pauseOnClick ) {
              self.pauseSlideshow();
            }
            self.showSlide( $slides.eq($(this).index()) );
          }
        });

        $theThumbnail.clone().removeClass("slide-background").removeAttr("id").appendTo($newThumb);

        $newThumb.appendTo($thumbnails);

      });

      $thumbnails.children().wrapAll('<div/>');

      if ( self.options.assignThumbnailsContainer === null ){
        $thumbnails.insertAfter(self.$elem);
      }

      self.options.assignThumbnailsContainer = $thumbnails;
    },

    showNextSlide: function() {
      var self = this,
          $slides = self.$elem.find("."+self._classes.slide),
          $current = $slides.filter("."+self.options.selectedSlideClass);

      if ( ($current.index() + 1) % $slides.length === 0 ) {
        // the last slide has been reached; jump to first slide
        self.showSlide($slides.first());
      } else {
        self.showSlide($current.next("."+self._classes.slide));
      }
    },

    showPreviousSlide: function() {
      var self = this,
        $slides = self.$elem.find("."+self._classes.slide),
        $current = $slides.filter("."+self.options.selectedSlideClass);

      if ( $current.index() === 0 ) {
        // we're at the first slide; jump to last slide
        self.showSlide($slides.last());
      } else {
        self.showSlide($current.prev("."+self._classes.slide));
      }
    },

    showSlide: function( $slide ) {
      var self = this,
        index = $slide.index();

      $slide.addClass(self.options.selectedSlideClass).fadeIn(self.options.transitionDuration)
        .siblings().removeClass(self.options.selectedSlideClass).fadeOut(self.options.transitionDuration);

      if ( self.options.showSlideIndicators ) {
        self.showSlideIndicator($slide);
      }

      if ( self.options.showSlideThumbnails ) {
        self.showSlideThumbnail($slide);
      }
    },

    showSlideIndicator: function( $slide ) {
      var self = this,
        index = $slide.index();

      if ( self.options.showSlideIndicators ) {
        self.$elem.find("."+self._classes.indicators).children().eq(index).addClass(self.options.selectedSlideClass)
          .siblings().removeClass(self.options.selectedSlideClass);
      }
    },

    showSlideThumbnail: function ( $slide ) {
      var self = this,
        index = $slide.index(),
        $thumbContainer = self.options.assignThumbnailsContainer, // self.$elem.next("."+self._classes.thumbnails),
        $thumb = $thumbContainer.find(".thumb").eq(index);

      if ( self.options.showSlideThumbnails ) {
        $thumb.addClass(self.options.selectedSlideClass)
          .siblings().removeClass(self.options.selectedSlideClass);

        self.slideToThumbnail( $thumbContainer, $thumb );
      }
    },

    slideToThumbnail: function ( $thumbContainer, $thumb ) {
      var self = this,
        leftScrollTo = -1,
        topScrollTo = -1,
        thumbPos = $thumb.position(),
        thumbWidth = $thumb.outerWidth(),
        thumbHeight = $thumb.outerHeight(),
        containerWidth = $thumbContainer.outerWidth(),
        containerHeight = $thumbContainer.outerHeight();

      if ( ((thumbWidth + thumbPos.left) > (containerWidth + $thumbContainer.scrollLeft()))
           || ($thumbContainer.scrollLeft() > thumbPos.left) ){
        leftScrollTo = thumbPos.left;
      }

      if ( ((thumbHeight + thumbPos.top) > (containerHeight + $thumbContainer.scrollTop()))
           || ($thumbContainer.scrollTop() > thumbPos.top) ){
        topScrollTo = thumbPos.top;
      }

      if ( leftScrollTo > -1 || topScrollTo > -1 ){
        $thumbContainer.animate({
          scrollLeft: leftScrollTo < 0 ? 0 : leftScrollTo,
          scrollTop: topScrollTo < 0 ? 0 : topScrollTo
        }, self.options.transitionDuration);
      }
    },

    playSlideshow: function() {
      var self = this;

      self.showNextSlide();

      self.showTimer = setTimeout(function() {
        self.playSlideshow();
      }, self.options.slideDuration);
    },

    pauseSlideshow: function() {
      var self = this;
      clearTimeout(self.showTimer);
    },

    getSlideCount: function() {
      var self = this,
          $slides = self.$elem.find("."+self._classes.slide);

      return $slides.length;
    }

  };

  $.fn.matrixBranding = function( options ) {
    return this.each(function() {

      var branding = Object.create( Branding );
      branding._init( options, this );

    });
  };

  $.fn.matrixBranding.options = {
    nextButtonClass: 'next',        // class name applied to the "next" button
    previousButtonClass: 'prev',    // class name applied to the "previous" button
    nextButtonText: '→',       // text for the auto-generated "next" button
    previousButtonText: '←',   // text for the auto-generated "previous" button
    selectedSlideClass: 'current',  // class name applied to the visible slide
    transitionDuration: 800,        // number in ms for how long a slide transition should be
    slideDuration: 7000,            // number in ms for how long a slide should show
    autoPlay: true,                 // set to false to prevent an automatic slideshow
    pauseOnClick: true,             // set to false to prevent pausing the slideshow on clicking slide navigation
    showSlideIndicators: false,     // set to true to automatically add a slide navigation list
    showSlideThumbnails: false,
    assignThumbnailsContainer: null,
    initialSlide: 1,                // explictly set a slide to show first
    startWithRandomSlide: false,    // set to true for a random initial slide
    assignNextNavigation: null,       // a jquery object to be binded as slide navigation
    assignPreviousNavigation: null    // a jquery object to be binded as slide navigation
  };
})( jQuery, window, document );

// Polyfill for Object.create method
if ( typeof Object.create !== 'function' ) {
  Object.create = function( obj ) {
    function F() {};
    F.prototype = obj;
    return new F();
  };
}
<|endoftext|>


<commit_before>myApp.controller('InventoryController', function($scope, $window, $location, $routeParams, inventoryService, breadCrumbService) {
    $scope.inventories = inventoryService.loadInventories();
    $scope.inventory = inventoryService.getInventory($routeParams.id);
    $scope.item = $scope.inventory[$routeParams.itemId];
    $scope.breadCrumbs = breadCrumbService;

    $scope.$on('$routeChangeSuccess', function (e, curr, prev) {
        var breadCrumb = curr.locals.breadCrumb;
        breadCrumb.path = $location.path().substring(1);
        breadCrumbService.update(breadCrumb);
    });

    $scope.goBack = function() {
        $window.history.go(-1);
    };

    $scope.editInventory = function(inv) {
        $location.path(`/edit/${inv.id}`);
    };

    $scope.newInventory = function() {
        var inventory = inventoryService.newInventory();
        $location.path(`/new/${inventory.id}`);
    };

    $scope.addInventory = function() {
        var id = $routeParams.id;
        $scope.inventories[id] = $scope.inventory;
        inventoryService.storeInventories();
        $location.path(`/view/${id}`);
    };

    $scope.newItem = function() {
        var id = $scope.inventory.id;
        var item = inventoryService.newItem();
        $location.path(`/edit/${id}/item/new/${item.id}`);
    };

    $scope.addItem = function() {
        var { id, itemId } = $routeParams;
        var inventory = $scope.inventories[id];

        inventory.items[itemId] = $scope.item;
        inventoryService.storeInventories();
        $location.path(`/view/${id}`);
    };

    $scope.removeItem = function() {
    };
});
<commit_msg>Fix setting .item incorrectly<commit_after>myApp.controller('InventoryController', function($scope, $window, $location, $routeParams, inventoryService, breadCrumbService) {
    $scope.inventories = inventoryService.loadInventories();
    $scope.inventory = inventoryService.getInventory($routeParams.id);
    if($scope.inventory && $routeParams.itemId)
        $scope.item = $scope.inventory[$routeParams.itemId];
    $scope.breadCrumbs = breadCrumbService;

    $scope.$on('$routeChangeSuccess', function (e, curr, prev) {
        var breadCrumb = curr.locals.breadCrumb;
        breadCrumb.path = $location.path().substring(1);
        breadCrumbService.update(breadCrumb);
    });

    $scope.goBack = function() {
        $window.history.go(-1);
    };

    $scope.editInventory = function(inv) {
        $location.path(`/edit/${inv.id}`);
    };

    $scope.newInventory = function() {
        var inventory = inventoryService.newInventory();
        $location.path(`/new/${inventory.id}`);
    };

    $scope.addInventory = function() {
        var id = $routeParams.id;
        $scope.inventories[id] = $scope.inventory;
        inventoryService.storeInventories();
        $location.path(`/view/${id}`);
    };

    $scope.newItem = function() {
        var id = $scope.inventory.id;
        var item = inventoryService.newItem();
        $location.path(`/edit/${id}/item/new/${item.id}`);
    };

    $scope.addItem = function() {
        var { id, itemId } = $routeParams;
        var inventory = $scope.inventories[id];

        inventory.items[itemId] = $scope.item;
        inventoryService.storeInventories();
        $location.path(`/view/${id}`);
    };

    $scope.removeItem = function() {
    };
});
<|endoftext|>


<commit_before>/**
 * Created by arlando on 7/27/14.
 */
/**
 * Particle experiments
 * - How many particles can we get rendered?
 * - How not to block the UI thread (50ms max execution time)?
 * - How can we expose properties to dynamically change the system?
 * - How can we keep particles independent of one another?
 */

'use strict';
var domready = require('domready');
var PIXI = require('pixi');
var Particle = require('./Particle');
var dat = require('dat');
var ParticleSystem = require('./ParticleSystem');
var drawStyles = require('./DrawStyles');
var updateFunctions = require('./PositionFunctions');
var curries = require('./Curries');
var SETTINGS = require('./SETTINGS');
var stage;
var particleSystem;
var renderer;
var gui;
var graphics;
var spriteBatch;

//Controllers
var numberOfParticlesController;

domready(function () {

    //TODO move somewhere else
    gui = new dat.GUI();

    renderer = new PIXI.autoDetectRenderer(SETTINGS.WIDTH, SETTINGS.HEIGHT, null, false, true);
    renderer.clearBeforeRender = false;
    document.body.appendChild(renderer.view);
    requestAnimFrame(animate);

    stage = new PIXI.Stage(0x000000);

    spriteBatch = new PIXI.SpriteBatch();
    stage.addChild(spriteBatch);

    drawStyles.setCircleTexture(new PIXI.Texture.fromImage('img/FFFFFF.png'));
    drawStyles.init(spriteBatch, PIXI.Sprite);
    gui.add(drawStyles.settings, 'blendMode', PIXI.blendModes);
    gui.add(drawStyles.settings, 'rot');

    //initialize ParticleSystem
    particleSystem = new ParticleSystem();
    gui.remember(particleSystem);

    //gravityController = gui.add(text, 'Gravity', 0, 10);
    numberOfParticlesController = gui.add(particleSystem, 'size', 0, 100000);

    numberOfParticlesController.onFinishChange(function (value) {
        particleSystem.size = Math.floor(value);
    });

    particleSystem.addParticles(particleSystem.size, Particle);
    //todo move this setup somewhere else
    //curries.addToSet(updateFunctions.gravity);
    //curries.addToSet(updateFunctions.walker);
    curries.addToSet(updateFunctions.basic);

    function animate() {
        requestAnimFrame( animate );

        //TODO: Consider exposing particles so you can use Duff's Device
        particleSystem
            .replenishParticles(Particle, spriteBatch)
            .update(curries.applicator)
            .draw(drawStyles.circle)
            .resetOrRemoveParticles(spriteBatch);

        renderer.render(stage);
    }
});

function initialize(number, spriteBatch) {
    particleSystem.addOrRemoveParticles(number, Particle, spriteBatch);
}<commit_msg>added bounding strategies to particles.js<commit_after>/**
 * Created by arlando on 7/27/14.
 */
/**
 * Particle experiments
 * - How many particles can we get rendered?
 * - How not to block the UI thread (50ms max execution time)?
 * - How can we expose properties to dynamically change the system?
 * - How can we keep particles independent of one another?
 */

'use strict';
var domready = require('domready');
var PIXI = require('pixi');
var Particle = require('./Particle');
var dat = require('dat');
var ParticleSystem = require('./ParticleSystem');
var drawStyles = require('./DrawStyles');
var updateFunctions = require('./PositionFunctions');
var boundingStrategies = require('./BoundingStrategies');
var curries = require('./Curries');
var SETTINGS = require('./SETTINGS');
var stage;
var particleSystem;
var renderer;
var gui;
var graphics;
var spriteBatch;

//Controllers
var numberOfParticlesController;

domready(function () {

    //TODO move somewhere else
    gui = new dat.GUI();

    renderer = new PIXI.autoDetectRenderer(SETTINGS.WIDTH, SETTINGS.HEIGHT, null, false, true);
    renderer.clearBeforeRender = false;
    document.body.appendChild(renderer.view);
    requestAnimFrame(animate);

    stage = new PIXI.Stage(0x000000);

    spriteBatch = new PIXI.SpriteBatch();
    stage.addChild(spriteBatch);

    drawStyles.setCircleTexture(new PIXI.Texture.fromImage('img/FFFFFF.png'));
    drawStyles.init(spriteBatch, PIXI.Sprite);
    gui.add(drawStyles.settings, 'blendMode', PIXI.blendModes);
    gui.add(drawStyles.settings, 'rot');

    //initialize ParticleSystem
    particleSystem = new ParticleSystem();
    gui.remember(particleSystem);

    //gravityController = gui.add(text, 'Gravity', 0, 10);
    numberOfParticlesController = gui.add(particleSystem, 'size', 0, 100000);

    numberOfParticlesController.onFinishChange(function (value) {
        particleSystem.size = Math.floor(value);
    });

    particleSystem.addParticles(particleSystem.size, Particle);
    //todo move this setup somewhere else
    curries.addToSet(boundingStrategies.bounceOffWall);
    //curries.addToSet(updateFunctions.gravity);
    curries.addToSet(updateFunctions.basic);
   // curries.addToSet(updateFunctions.walker);

    function animate() {
        requestAnimFrame( animate );

        //TODO: Consider exposing particles so you can use Duff's Device
        particleSystem
            .replenishParticles(Particle, spriteBatch)
            .update(curries.applicator)
            .draw(drawStyles.circle)
            .resetOrRemoveParticles(spriteBatch);

        renderer.render(stage);
    }
});

function initialize(number, spriteBatch) {
    particleSystem.addOrRemoveParticles(number, Particle, spriteBatch);
}<|endoftext|>


<commit_before>'use strict';

angular.module('copayApp.controllers').controller('SidebarController', function($scope, $rootScope, $sce, $location, $http, $filter, notification, controllerUtils) {

  $scope.menu = [{
    'title': 'Receive',
    'icon': 'fi-download',
    'link': 'receive'
  }, {
    'title': 'Send',
    'icon': 'fi-arrow-right',
    'link': 'send'
  }, {
    'title': 'History',
    'icon': 'fi-clipboard-pencil',
    'link': 'history'
  }, {
    'title': 'Settings',
    'icon': 'fi-widget',
    'link': 'more'
  }];

  $scope.signout = function() {
    logout();
  };

  // Ensures a graceful disconnect
  window.onbeforeunload = function() {
    controllerUtils.logout();
  };

  $scope.$on('$destroy', function() {
    window.onbeforeunload = undefined;
  });

  $scope.refresh = function() {
    var w = $rootScope.wallet;
    if (w.isReady()) {
      w.sendWalletReady();
      if ($rootScope.addrInfos.length > 0) {
        controllerUtils.clearBalanceCache(w);
        controllerUtils.updateBalance(w, function() {
          $rootScope.$digest();
        });
      }
    }
  };

  $scope.isActive = function(item) {
    return item.link && item.link == $location.path().split('/')[1];
  };

  function logout() {
    controllerUtils.logout();
  }

  // ng-repeat defined number of times instead of repeating over array?
  $scope.getNumber = function(num) {
    return new Array(num);
  }

  if ($rootScope.wallet) {
    $scope.$on('$idleWarn', function(a, countdown) {
      if (!(countdown % 5))
        notification.warning('Session will be closed', $filter('translate')('Your session is about to expire due to inactivity in') + ' ' + countdown + ' ' + $filter('translate')('seconds'));
    });

    $scope.$on('$idleTimeout', function() {
      $scope.signout();
      notification.warning('Session closed', 'Session closed because a long time of inactivity');
    });
    $scope.$on('$keepalive', function() {
      $rootScope.wallet.keepAlive();
    });
  }

  $scope.switchWallet = function(wid) {
    $scope.walletSelection = false;
    controllerUtils.setFocusedWallet(wid);
  };

  $scope.toggleWalletSelection = function() {
    $scope.walletSelection = !$scope.walletSelection;
    $scope.wallets = [];
    var wids = _.pluck($rootScope.iden.listWallets(), 'id');
    _.each(wids, function(wid) {
      if (controllerUtils.isFocusedWallet(wid)) return;
      var w = $rootScope.iden.getOpenWallet(wid);
      $scope.wallets.push(w);
      controllerUtils.updateBalance(w, function(err, res) {
        if (err) return;
        setTimeout(function() {
          $scope.$digest();
        }, 1);
      });
    });
  };
});
<commit_msg>compute balance only if list of wallets is visible<commit_after>'use strict';

angular.module('copayApp.controllers').controller('SidebarController', function($scope, $rootScope, $sce, $location, $http, $filter, notification, controllerUtils) {

  $scope.menu = [{
    'title': 'Receive',
    'icon': 'fi-download',
    'link': 'receive'
  }, {
    'title': 'Send',
    'icon': 'fi-arrow-right',
    'link': 'send'
  }, {
    'title': 'History',
    'icon': 'fi-clipboard-pencil',
    'link': 'history'
  }, {
    'title': 'Settings',
    'icon': 'fi-widget',
    'link': 'more'
  }];

  $scope.signout = function() {
    logout();
  };

  // Ensures a graceful disconnect
  window.onbeforeunload = function() {
    controllerUtils.logout();
  };

  $scope.$on('$destroy', function() {
    window.onbeforeunload = undefined;
  });

  $scope.refresh = function() {
    var w = $rootScope.wallet;
    if (w.isReady()) {
      w.sendWalletReady();
      if ($rootScope.addrInfos.length > 0) {
        controllerUtils.clearBalanceCache(w);
        controllerUtils.updateBalance(w, function() {
          $rootScope.$digest();
        });
      }
    }
  };

  $scope.isActive = function(item) {
    return item.link && item.link == $location.path().split('/')[1];
  };

  function logout() {
    controllerUtils.logout();
  }

  // ng-repeat defined number of times instead of repeating over array?
  $scope.getNumber = function(num) {
    return new Array(num);
  }

  if ($rootScope.wallet) {
    $scope.$on('$idleWarn', function(a, countdown) {
      if (!(countdown % 5))
        notification.warning('Session will be closed', $filter('translate')('Your session is about to expire due to inactivity in') + ' ' + countdown + ' ' + $filter('translate')('seconds'));
    });

    $scope.$on('$idleTimeout', function() {
      $scope.signout();
      notification.warning('Session closed', 'Session closed because a long time of inactivity');
    });
    $scope.$on('$keepalive', function() {
      $rootScope.wallet.keepAlive();
    });
  }

  $scope.switchWallet = function(wid) {
    $scope.walletSelection = false;
    controllerUtils.setFocusedWallet(wid);
  };

  $scope.toggleWalletSelection = function() {
    $scope.walletSelection = !$scope.walletSelection;
    if (!$scope.walletSelection) return;

    $scope.wallets = [];
    var wids = _.pluck($rootScope.iden.listWallets(), 'id');
    _.each(wids, function(wid) {
      if (controllerUtils.isFocusedWallet(wid)) return;
      var w = $rootScope.iden.getOpenWallet(wid);
      $scope.wallets.push(w);
      controllerUtils.updateBalance(w, function(err, res) {
        if (err) return;
        setTimeout(function() {
          $scope.$digest();
        }, 1);
      });
    });
  };
});
<|endoftext|>