Last active
August 16, 2016 00:33
-
-
Save Beej126/1501d5acb4fd20a6fcdcfe6599ce0c5e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function (main, modules) { | |
'use strict'; | |
var cache = {}; | |
var wd = function (c) { | |
return c.split(/\//).slice(0, -1).join('/'); | |
}; | |
window.process = { | |
cwd: function () { | |
return wd(location.pathname) || '/'; | |
} | |
}; | |
var require = function (id) { | |
var exports = cache[id]; | |
if (!exports) { | |
var module = modules[id]; | |
if (!module) | |
throw new Error('module ' + id + ' not found'); | |
var mod = {}; | |
exports = mod.exports = {}; | |
var cwd = wd(location.pathname), mwd = wd(id); | |
var __dirname = mwd ? cwd + '/' + mwd : cwd, __filename = cwd + '/' + id; | |
module.call(exports, require, mod, exports, window, __filename, __dirname); | |
exports = cache[id] = mod.exports; | |
} | |
return exports; | |
}; | |
var atLoc = /(@loc)\b/g; | |
var join_ = Array.prototype.join; | |
var resolve = function (id, lang, script, region) { | |
var attempt = function (m) { | |
return modules[m] ? m : null; | |
}; | |
var options = []; | |
var add = function (option) { | |
option = join_.call(arguments, '-'); | |
if (options.indexOf(option) === -1) | |
options.push(option); | |
}; | |
if (region) { | |
add(lang, script, region); | |
add(lang, region); | |
} | |
if (script) | |
add(lang, script); | |
add(lang); | |
add('en'); | |
var i, key; | |
for (i = 0; i < supportedLanguages.length; i++) | |
add(supportedLanguages[i]); | |
for (i = 0; i < options.length; i++) { | |
var m = id.replace(atLoc, options[i]); | |
if (key = attempt(m)) | |
return key; | |
} | |
return m; | |
}; | |
var done = function (LANG) { | |
var loc = LANG.replace(/_/g, '-').toLowerCase().split('-'); | |
var lang = loc[0]; | |
var script = loc[1]; | |
var region = loc[2]; | |
var _require = require; | |
require = function (id) { | |
return _require(id.match(atLoc) ? resolve(id, lang, script, region) : id); | |
}; | |
require(main); | |
}; | |
require('node_modules/quickstart-spotify/api-core.js'); | |
try { | |
var supportedLanguages = require('supported-languages.json'); | |
} catch (err) { | |
} | |
if (supportedLanguages) { | |
SP.request('session_query', [], null, function (data) { | |
done(data.language); | |
}, function () { | |
done('en'); | |
}); | |
} else { | |
done('en'); | |
} | |
}('scripts/main.js', { | |
'node_modules/api/scripts/core.js': function (require, module, exports, global, __filename, __dirname) { | |
var spotify = {}; | |
function SpotifyApi() { | |
this._modules = {}; | |
this._requested = {}; | |
this._moduleQueue = []; | |
this._delayedFns = []; | |
this._parallelReqs = 4; | |
this._contextStack = []; | |
this._deferredFlush = false; | |
this._useLoadingTimeout = false; | |
this._patchRequestOpen(); | |
} | |
SpotifyApi.AnalyticsContext = function (name) { | |
this.name = name; | |
this.id = SpotifyApi.AnalyticsContext._nextId++; | |
this.references = 0; | |
this._begin(); | |
}; | |
SpotifyApi.AnalyticsContext._nextId = 1; | |
SpotifyApi.AnalyticsContext.prototype.addReference = function () { | |
this.references++; | |
}; | |
SpotifyApi.AnalyticsContext.prototype.removeReference = function () { | |
this.references--; | |
if (this.references === 0) { | |
this._end(); | |
} | |
}; | |
SpotifyApi.AnalyticsContext.prototype._begin = function () { | |
SpotifyApi.api.request('core_context_begin', [ | |
this.id, | |
this.name | |
], this); | |
}; | |
SpotifyApi.AnalyticsContext.prototype._end = function () { | |
SpotifyApi.api.request('core_context_end', [this.id], this); | |
}; | |
SpotifyApi.prototype.analyticsContext = function (name, func) { | |
var context = new SpotifyApi.AnalyticsContext(name); | |
context.addReference(); | |
this._contextStack.push(context); | |
try { | |
func(); | |
} finally { | |
this._contextStack.pop(); | |
context.removeReference(); | |
} | |
}; | |
SpotifyApi.Callback = function (func, opt_contextStack) { | |
this._func = func; | |
this._setContextStack(opt_contextStack || SpotifyApi.api._contextStack); | |
}; | |
SpotifyApi.Callback.prototype.apply = function (context, args) { | |
try { | |
var oldContextStack = SpotifyApi.api._contextStack; | |
SpotifyApi.api._contextStack = this._contextStack; | |
this._func.apply(context, args); | |
} catch (error) { | |
setTimeout(function () { | |
throw error; | |
}, 0); | |
} finally { | |
SpotifyApi.api._contextStack = oldContextStack; | |
this.clear(); | |
} | |
}; | |
SpotifyApi.Callback.prototype.call = function (context, var_args) { | |
this.apply(context, Array.prototype.slice.call(arguments, 1)); | |
}; | |
SpotifyApi.Callback.prototype.copy = function () { | |
return new this.constructor(this._func, this._contextStack); | |
}; | |
SpotifyApi.Callback.prototype.clear = function () { | |
this._releaseContextStack(); | |
delete this._func; | |
delete this._contextStack; | |
}; | |
SpotifyApi.Callback.prototype._setContextStack = function (contextStack) { | |
for (var i = 0, l = contextStack.length; i < l; ++i) { | |
contextStack[i].addReference(); | |
} | |
this._contextStack = contextStack.slice(0); | |
}; | |
SpotifyApi.Callback.prototype._releaseContextStack = function () { | |
var contextStack = this._contextStack; | |
for (var i = 0, l = contextStack.length; i < l; ++i) { | |
contextStack[l - i - 1].removeReference(); | |
} | |
}; | |
SpotifyApi.prototype.callback = function (func) { | |
return new SpotifyApi.Callback(func); | |
}; | |
SpotifyApi.prototype._getContextIdForRequest = function () { | |
var contexts = this._contextStack; | |
return contexts.length ? contexts[contexts.length - 1].id : 0; | |
}; | |
window.addEventListener('message', function (event) { | |
if (event.source == window && event.data == 'api-delay') { | |
event.stopPropagation(); | |
var functions = SpotifyApi.api._delayedFns.splice(0); | |
for (var i = 0, l = functions.length; i < l; i++) { | |
functions[i].call(); | |
} | |
} | |
}); | |
SpotifyApi.prototype._prepareFlush = function (name) { | |
if (!this._deferredFlush && name != 'core_flush') { | |
this._deferredFlush = true; | |
this.defer(this, this._flushRequests); | |
} | |
}; | |
SpotifyApi.prototype._flushRequests = function () { | |
this._deferredFlush = false; | |
this.request('core_flush', []); | |
}; | |
SpotifyApi.prototype.defer = function (self, func) { | |
if (this._delayedFns.push(this.bind(this.callback(func), self)) == 1) | |
window.postMessage('api-delay', '*'); | |
}; | |
SpotifyApi.prototype._evalModule = function (meta, graph, module, code) { | |
return !/\.lang$/.test(module) ? this._evalJSModule(meta, graph, module, code) : this._evalLangModule(module, code); | |
}; | |
SpotifyApi.prototype._evalJSModule = function (meta, graph, module, code) { | |
var self = this; | |
var exports = { __name: module }; | |
var require = function (modules, fn) { | |
exports.__waiting = true; | |
var callback = function () { | |
exports.__waiting = false; | |
return fn.apply(this, arguments); | |
}; | |
callback.__native = true; | |
return self._require(module, meta, graph, modules, callback); | |
}; | |
try { | |
code = '\'use strict\';' + code + '\n//@ sourceURL=' + module; | |
new Function('require', 'exports', 'SP', '_code', 'eval(_code)').call({}, require, exports, this, code); | |
return exports; | |
} catch (error) { | |
error.message += ' in ' + module; | |
throw error; | |
} | |
}; | |
SpotifyApi.LangModule = function (name, strings) { | |
this.__name = name; | |
this.strings = strings; | |
}; | |
SpotifyApi.LangModule.prototype.get = function (key, var_args) { | |
var format = this.strings.hasOwnProperty(key) ? this.strings[key] : key; | |
var out = '', lastIndex = 0, startIndex, endIndex; | |
while ((startIndex = format.indexOf('{', lastIndex)) > -1) { | |
endIndex = format.indexOf('}', startIndex + 1); | |
if (endIndex == -1) { | |
break; | |
} | |
var value = arguments[parseInt(format.substring(startIndex + 1, endIndex)) + 1]; | |
if (value !== undefined) { | |
out += format.substring(lastIndex, startIndex) + value; | |
} else { | |
out += format.substring(lastIndex, endIndex + 1); | |
} | |
lastIndex = endIndex + 1; | |
} | |
return lastIndex ? out + format.substring(lastIndex) : format; | |
}; | |
SpotifyApi.prototype._evalLangModule = function (module, code) { | |
try { | |
return new SpotifyApi.LangModule(module, JSON.parse(code)); | |
} catch (error) { | |
throw new Error('Cannot import language file "' + module + '": ' + error.message); | |
} | |
}; | |
SpotifyApi.prototype._fireCallbacks = function (meta) { | |
while (meta) { | |
meta.waiting--; | |
if (meta.waiting) | |
break; | |
meta.unpacked.forEach(function (unpacked) { | |
var pos = unpacked.position; | |
var exported = meta.args[pos]; | |
var property = unpacked.property; | |
if (!(property in exported)) | |
throw new Error('No "' + property + '" exported in module "' + exported.__name + '"'); | |
meta.args[pos] = exported[property]; | |
}); | |
meta.callback.apply({}, meta.args); | |
meta.waiting = 1 / 0; | |
meta = meta.parent; | |
} | |
}; | |
SpotifyApi.prototype._createRequest = function (path, callback) { | |
var request, timeoutMS, xmlHttpTimeout, timedOut; | |
request = new XMLHttpRequest(); | |
request.open('GET', path, true); | |
request.onreadystatechange = function () { | |
var isDone, iOSHack, isOK; | |
isDone = request.readyState === 4; | |
if (isDone) { | |
clearTimeout(xmlHttpTimeout); | |
if (timedOut) { | |
throw new Error('Could not load file "' + path + '"; Timed out.'); | |
} | |
iOSHack = request.status === 0 && !!request.responseText; | |
isOK = request.status === 200 || iOSHack; | |
if (!isOK) { | |
throw new Error('Could not load file "' + path + '"; Not found.'); | |
} | |
callback(request.responseText); | |
} | |
}; | |
if (this._useLoadingTimeout) { | |
timeoutMS = 1500; | |
xmlHttpTimeout = setTimeout(function () { | |
timedOut = true; | |
request.abort(); | |
}, timeoutMS); | |
} | |
request.send(null); | |
}; | |
SpotifyApi.prototype._loadModule = function (meta, graph, module, position, property) { | |
var self = this; | |
var cached = this._modules[module]; | |
if (cached && !cached.__waiting) { | |
meta.args[position] = this._modules[module]; | |
if (property) | |
meta.unpacked.push({ | |
property: property, | |
position: position | |
}); | |
this._fireCallbacks(meta); | |
} else if (this._requested[module] || !this._parallelReqs) { | |
this.defer(this, function () { | |
this._loadModule(meta, graph, module, position, property); | |
}); | |
} else { | |
this._requested[module] = true; | |
this._parallelReqs--; | |
this._createRequest(module, function (responseText) { | |
self._parallelReqs++; | |
var exported = self._modules[module] = self._evalModule(meta, graph, module, responseText); | |
meta.args[position] = exported; | |
if (property) | |
meta.unpacked.push({ | |
property: property, | |
position: position | |
}); | |
self._fireCallbacks(meta); | |
}); | |
} | |
}; | |
SpotifyApi.prototype._resolveModule = function (module) { | |
if (!/\.lang$/.test(module)) { | |
var _module = module.match(/^(\$(?:[^\/]+)\/)(?!scripts)(.*)/); | |
if (_module) | |
module = _module[1] + 'scripts/' + _module[2]; | |
module += '.js'; | |
} | |
return module; | |
}; | |
SpotifyApi.prototype._require = function (name, parent, graph, modules, fn) { | |
if (typeof modules == 'string') | |
modules = [modules]; | |
if (!modules || !modules.length) | |
throw new Error('Missing modules argument to require().'); | |
if (!fn || typeof fn != 'function') | |
throw new Error('Missing callback function argument to require().'); | |
var len = modules.length; | |
var meta = { | |
name: name, | |
parent: parent, | |
waiting: len, | |
callback: fn, | |
args: new Array(len), | |
unpacked: [] | |
}; | |
parent.waiting++; | |
for (var i = 0, l = len; i < l; i++) { | |
var module = modules[i]; | |
if (!module) | |
throw new Error('Empty module name in require.'); | |
var property = module.split('#'); | |
module = this._resolveModule(property[0]); | |
property = property[1]; | |
var modGraph = graph.slice(0); | |
var index = graph.indexOf(module); | |
modGraph.push(module); | |
if (index != -1) { | |
modGraph = modGraph.slice(index).join(' -> '); | |
throw new Error('Circular Dependency on Module "' + module + '": ' + modGraph); | |
} | |
this._loadModule(meta, modGraph, module, i, property); | |
} | |
}; | |
SpotifyApi.prototype.varargs = function (values, opt_offset, opt_copy) { | |
if (!opt_offset) | |
opt_offset = 0; | |
if (Array.isArray(values[opt_offset])) { | |
if (values.length > opt_offset + 1) | |
throw new Error('Ambiguous use of varargs'); | |
values = values[opt_offset]; | |
opt_offset = 0; | |
} | |
return opt_offset || opt_copy ? Array.prototype.slice.call(values, opt_offset) : values; | |
}; | |
SpotifyApi.prototype.uris = function (values, opt_offset) { | |
var objs = this.varargs(values, opt_offset), uris = []; | |
for (var i = 0, len = objs.length; i < len; i++) { | |
uris.push(objs[i].uri); | |
} | |
return uris; | |
}; | |
SpotifyApi.prototype.bind = function (func, that, var_args) { | |
if (arguments.length > 2) { | |
var slice = Array.prototype.slice; | |
var bind = Function.prototype.bind; | |
if (bind && func.bind === bind) | |
return bind.apply(func, slice.call(arguments, 1)); | |
var args = slice.call(arguments, 2); | |
return function () { | |
return func.apply(that, arguments.length ? args.concat(slice.call(arguments)) : args); | |
}; | |
} else { | |
return function () { | |
return func.apply(that, arguments); | |
}; | |
} | |
}; | |
SpotifyApi.prototype.inherit = function (childConstructor, parentConstructor) { | |
var TempConstructor = function () { | |
}; | |
TempConstructor.prototype = childConstructor._superClass = parentConstructor.prototype; | |
childConstructor.prototype = new TempConstructor(); | |
childConstructor.prototype.constructor = childConstructor; | |
return childConstructor; | |
}; | |
SpotifyApi.prototype._patchRequestOpen = function () { | |
var open = XMLHttpRequest.prototype.open; | |
var link = document.createElement('a'); | |
var location = window.location; | |
XMLHttpRequest.prototype.open = function (method, url) { | |
var result = open.apply(this, arguments); | |
link.href = url; | |
if (link.protocol == ':' && !link.hostname || link.protocol == location.protocol && link.hostname == location.hostname) { | |
this.setRequestHeader('X-Spotify-Requested-With', 'XMLHttpRequest'); | |
} | |
return result; | |
}; | |
}; | |
SpotifyApi.prototype.resolvePath = function (path) { | |
return path; | |
}; | |
function require(modules, callback) { | |
return SpotifyApi.api._require('__main__', { | |
callback: function () { | |
}, | |
waiting: 1 / 0 | |
}, [], modules, callback); | |
} | |
spotify.require = require; | |
String; | |
String.prototype.decodeForText = function () { | |
return this.toString(); | |
}; | |
String.prototype.decodeForHtml = function () { | |
var e = { | |
'&': '&', | |
'<': '<', | |
'>': '>' | |
}; | |
var r = function (c) { | |
return e[c]; | |
}; | |
return function () { | |
return this.replace(/[&<>]/g, r); | |
}; | |
}(); | |
String.prototype.decodeForLink = function () { | |
return encodeURI(this); | |
}; | |
SpotifyApi.Bases = { | |
uri: 'spotify', | |
url: 'http://open.spotify.com' | |
}; | |
SpotifyApi.Exps = { | |
spotify: /^spotify:(.+)$/, | |
http: /^https?:\/\/(play|open)\.spotify\.com\/(.+)$/ | |
}; | |
String.prototype.toSpotifyURL = function () { | |
var matches = this.match(SpotifyApi.Exps.spotify); | |
if (!matches) | |
return this; | |
var parts = matches.pop().replace(/:$/, '').split(/:/); | |
var type = parts.shift(); | |
if (type == 'search') | |
parts = [parts.join(':')]; | |
parts.unshift(SpotifyApi.Bases.url, type); | |
return parts.join('/'); | |
}; | |
String.prototype.toSpotifyURI = function () { | |
var matches = this.match(SpotifyApi.Exps.http); | |
if (!matches) | |
return this; | |
var parts = matches.pop().replace(/\/$/, '').split(/\//); | |
parts.unshift(SpotifyApi.Bases.uri); | |
return parts.join(':'); | |
}; | |
String.prototype.toSpotifyLink = function () { | |
return this.toSpotifyURI(); | |
}; | |
module.exports = SpotifyApi; | |
}, | |
'node_modules/api/scripts/core.desktop.js': function (require, module, exports, global, __filename, __dirname) { | |
(function () { | |
SpotifyApi.prototype._throwError = true; | |
var bridge = window._getSpotifyModule('bridge'); | |
var core; | |
try { | |
core = window._getSpotifyModule('core'); | |
} catch (err) { | |
} | |
if (core) | |
SpotifyApi.prototype._createRequest = function (module, callback) { | |
this.defer(this, function () { | |
var code = core.readFile(module); | |
if (undefined === code) { | |
throw new Error('Could not load module "' + module + '"; Not found.'); | |
} else { | |
callback(code); | |
} | |
}); | |
}; | |
SpotifyApi.prototype.request = function (name, args, caller, success, failed) { | |
var contextId = this._getContextIdForRequest(); | |
var message = JSON.stringify({ | |
name: name, | |
args: args, | |
context: contextId | |
}); | |
bridge.executeRequest(message, { | |
onSuccess: function (data) { | |
if (success) { | |
success.call(caller, JSON.parse(data)); | |
} | |
}, | |
onFailure: function (data) { | |
data = JSON.parse(data); | |
if (failed) { | |
failed.call(caller, data); | |
} | |
} | |
}); | |
this._prepareFlush(name); | |
}; | |
SpotifyApi.api = new SpotifyApi(); | |
SpotifyApi.api.container = 'desktop'; | |
}()); | |
}, | |
'node_modules/api/scripts/core.browser.js': function (require, module, exports, global, __filename, __dirname) { | |
(function () { | |
var uid = 0; | |
var callbacks = {}; | |
SpotifyApi.prototype._throwError = true; | |
SpotifyApi.prototype._useLoadingTimeout = true; | |
var manifest = window.manifest; | |
var appVendor = manifest && manifest.VendorIdentifier || 'unknown'; | |
var appVersion = manifest && manifest.BundleVersion || ''; | |
if (!appVersion.match(/^\d+\.\d+\.\d+$/)) { | |
appVersion = '0.0.0'; | |
} | |
var deps = window.dependencies; | |
var staticDeps = deps['static']; | |
var rootDepsBare = staticDeps.replace(/\/([^\/]*)$/, ''); | |
var rootDeps = rootDepsBare + '/'; | |
var preferredLocales = ['en.loc']; | |
var localeStringMatch = window.location.search.match(/locale=([^&]+)/); | |
if (localeStringMatch) { | |
preferredLocales = localeStringMatch.pop().split(','); | |
var len = preferredLocales.length; | |
while (len--) { | |
var preferredLocale = preferredLocales[len]; | |
preferredLocales[len] = preferredLocale.indexOf('.loc') != -1 ? preferredLocale : preferredLocale + '.loc'; | |
} | |
} | |
var locale; | |
var localeFilesAvailable = {}; | |
var defaultLocale = 'en'; | |
if (deps.locale && !Array.isArray(deps.locale)) { | |
var locales = deps.locale; | |
for (var localeName in locales) { | |
locale = locales[localeName]; | |
localeName = localeName + '.loc'; | |
for (var i = 0, l = locale.length; i < l; i++) { | |
localeFilesAvailable[localeName + '/' + locale[i]] = true; | |
} | |
localeFilesAvailable[localeName + '/scripts/momentLang.js'] = true; | |
} | |
for (var x = 0, y = preferredLocales.length; x < y; x++) { | |
locale = preferredLocales[x].replace('.loc', ''); | |
if (deps.locale[locale] && deps.locale[locale].length) { | |
defaultLocale = locale; | |
break; | |
} | |
} | |
} | |
if (window.manifest && Array.isArray(manifest.SupportedLanguages)) { | |
var supportedLanguages = manifest.SupportedLanguages; | |
for (var m = 0, n = preferredLocales.length; m < n; m++) { | |
locale = preferredLocales[m].replace('.loc', ''); | |
var index = supportedLanguages.indexOf(locale); | |
if (index != -1) { | |
defaultLocale = locale; | |
break; | |
} | |
} | |
} | |
var resolve = SpotifyApi.prototype._resolveModule; | |
SpotifyApi.prototype._resolveModule = function (module) { | |
var result = resolve(module); | |
var match = result.match(/^\$([a-z\-\_]+)(\/.*)/); | |
var framework = false, path, leadingSlash = false; | |
if (match) { | |
framework = match[1]; | |
path = match[2]; | |
} else if (/^\//.exec(result)) { | |
leadingSlash = true; | |
} | |
var lang = false; | |
if (/\.lang$/.exec(result) || /momentLang\.js$/.exec(result)) { | |
if (framework) { | |
lang = preferredLocales[0]; | |
result = '$' + framework + '/' + (path = '/' + lang + path); | |
} else { | |
result = /^\//.test(result) ? result : '/' + result; | |
var file = ''; | |
for (var i = 0, l = preferredLocales.length; i < l; i++) { | |
lang = preferredLocales[i]; | |
file = lang + result; | |
if (localeFilesAvailable[file]) | |
break; | |
} | |
result = (leadingSlash ? '/' : '') + file; | |
} | |
} | |
if (framework && deps[framework]) { | |
result = deps[framework] + path; | |
} else { | |
if (framework) | |
result = '/' + framework + path; | |
else if (!leadingSlash) | |
result = '/' + result; | |
result = (framework ? rootDepsBare : staticDeps) + result; | |
} | |
return result; | |
}; | |
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; | |
if (MutationObserver) { | |
var observer = new MutationObserver(function (mutations) { | |
for (var j = 0, m = mutations.length; j < m; j++) { | |
var mutation = mutations[j]; | |
var links = mutation.addedNodes; | |
if (!links.length) | |
return this; | |
var matcher = staticDeps + '/$'; | |
for (var i = 0, l = links.length; i < l; i++) { | |
var link = links[i]; | |
if (link.tagName.toLowerCase() != 'link' || !/^\$/.test(link.getAttribute('href'))) | |
continue; | |
var href = link.href; | |
link.href = href.replace(matcher, rootDeps); | |
} | |
} | |
}); | |
observer.observe(document.head, { childList: true }); | |
} else { | |
var listenSubtree = function (event) { | |
if (event.target !== document.head) | |
return; | |
var links = document.head.querySelectorAll('link[href^="$"]'); | |
var matcher = staticDeps + '/$'; | |
for (var i = 0, l = links.length; i < l; i++) { | |
var link = links[i]; | |
if (!/^\$/.test(link.getAttribute('href'))) | |
continue; | |
var href = link.href; | |
link.href = href.replace(matcher, rootDeps); | |
} | |
}; | |
document.head.addEventListener('DOMSubtreeModified', listenSubtree); | |
} | |
if ('XDomainRequest' in window) { | |
var createXHR = SpotifyApi.prototype._createRequest; | |
SpotifyApi.prototype._createRequest = function (module, callback) { | |
if (!/^http/.test(module)) | |
return createXHR(module, callback); | |
var request = new XDomainRequest(); | |
request.onprogress = function () { | |
}; | |
request.onerror = function () { | |
throw new Error('Could not load module "' + module + '"; Not found.'); | |
}; | |
request.onload = function () { | |
callback(request.responseText); | |
}; | |
request.open('GET', module); | |
request.send(null); | |
}; | |
} | |
var sendDependencies = { hermes_register_schema: 1 }; | |
SpotifyApi.prototype.request = function (name, args, caller, success, failed) { | |
var top = window.top; | |
if (top === window) | |
return this; | |
var data = { | |
type: 'bridge_request', | |
id: uid++, | |
name: name, | |
args: args, | |
appVendor: appVendor, | |
appVersion: appVersion | |
}; | |
if (sendDependencies[name]) | |
data.deps = deps; | |
if (name == 'session_query') { | |
var oldSuccess = success; | |
success = function (payload) { | |
if (payload) { | |
payload.language = defaultLocale; | |
} | |
return oldSuccess.call(this, payload); | |
}; | |
} | |
top.postMessage(JSON.stringify(data), '*'); | |
if (!success) | |
return this; | |
callbacks[data.id] = { | |
success: success, | |
failed: failed, | |
caller: caller | |
}; | |
this._prepareFlush(name); | |
}; | |
SpotifyApi.prototype._requestReply = function (e) { | |
var data = e.data; | |
if (typeof data == 'string') { | |
try { | |
data = JSON.parse(data); | |
} catch (e) { | |
return this; | |
} | |
} | |
var callback = callbacks[data.id]; | |
if (!callback) | |
return this; | |
if (data.success && callback.success) | |
callback.success.call(callback.caller, data.payload); | |
else if (!data.success && callback.failed) | |
callback.failed.call(callback.caller, data.payload); | |
}; | |
SpotifyApi.prototype.resolvePath = function (path) { | |
var dummyExtension = '._resolve_'; | |
var resolvedPath = this._resolveModule(path + dummyExtension); | |
var originalSplit = path.split('.'); | |
if (originalSplit.length > 1) { | |
var extension = originalSplit[originalSplit.length - 1]; | |
if (extension !== 'js') { | |
resolvedPath = resolvedPath.replace('scripts/', ''); | |
} | |
var jsExtension = '.js'; | |
resolvedPath = resolvedPath.slice(0, -(dummyExtension.length + jsExtension.length)); | |
} else { | |
resolvedPath = resolvedPath.replace(dummyExtension, ''); | |
} | |
return resolvedPath; | |
}; | |
SpotifyApi.api = new SpotifyApi(); | |
SpotifyApi.api.container = 'web'; | |
window.addEventListener('message', SpotifyApi.api._requestReply, false); | |
SpotifyApi.Bases.url = 'https://play.spotify.com'; | |
String.prototype.toSpotifyLink = function () { | |
return this.toSpotifyURL(); | |
}; | |
document.documentElement.addEventListener('click', function (e) { | |
var target = e.target; | |
do { | |
if (target.nodeName.toLowerCase() === 'a') { | |
break; | |
} | |
} while ((target = target.parentNode) && target !== document.body); | |
if (!target || target === document.body) | |
return; | |
var href = target.href; | |
var uri = null; | |
if (SpotifyApi.Exps.http.test(href)) { | |
uri = href.toSpotifyURI(); | |
} else if (SpotifyApi.Exps.spotify.test(href)) { | |
uri = href; | |
} | |
if (!uri) | |
return; | |
if (e.defaultPrevented) | |
return; | |
e.preventDefault(); | |
SpotifyApi.api.request('application_open_uri', [ | |
uri, | |
null | |
]); | |
}); | |
var slice = Array.prototype.slice; | |
if (!Array.prototype.indexOf) { | |
Array.prototype.indexOf = function (item, from) { | |
var length = this.length >>> 0; | |
for (var i = from < 0 ? Math.max(0, length + from) : from || 0; i < length; i++) { | |
if (this[i] === item) | |
return i; | |
} | |
return -1; | |
}; | |
} | |
if (!String.prototype.trim) { | |
String.prototype.trim = function () { | |
return String(this).replace(/^\s+|\s+$/g, ''); | |
}; | |
} | |
if (!Function.prototype.bind) { | |
Function.prototype.bind = function (that) { | |
var self = this, args = arguments.length > 1 ? slice.call(arguments, 1) : null, F = function () { | |
}; | |
var bound = function () { | |
var context = that, length = arguments.length; | |
if (this instanceof bound) { | |
F.prototype = self.prototype; | |
context = new F(); | |
} | |
var result = !args && !length ? self.call(context) : self.apply(context, args && length ? args.concat(slice.call(arguments)) : args || arguments); | |
return context == that ? result : context; | |
}; | |
return bound; | |
}; | |
} | |
(function () { | |
if (!window.metadata) | |
return; | |
var appid = '[' + window.metadata.identifier + ' ' + window.metadata.version + ']'; | |
var console = window.console; | |
var apply = Function.prototype.apply; | |
var patch = [ | |
'debug', | |
'error', | |
'info', | |
'log', | |
'warn' | |
]; | |
if (!console) | |
return; | |
patch.forEach(function (p) { | |
var origFunc = console[p]; | |
if (!origFunc) | |
return; | |
console[p] = function () { | |
var args = slice.call(arguments); | |
if (typeof args[0] === 'string') { | |
args[0] = appid + ' ' + args[0]; | |
} else { | |
args.unshift(appid); | |
} | |
return apply.call(origFunc, console, args); | |
}; | |
}); | |
}()); | |
var kbd = { | |
_modifiers: {}, | |
_keymap: {}, | |
_ignore: {}, | |
_bindings: {}, | |
_empty: function () { | |
}, | |
init: function () { | |
SpotifyApi.api.request('keyboard_get_bindings', [], this, function (directives) { | |
for (var i in directives) { | |
if (!directives.hasOwnProperty(i)) | |
continue; | |
this[i] = directives[i]; | |
} | |
}.bind(this), this._empty); | |
window.addEventListener('keydown', this.handleOwn.bind(this, false)); | |
window.addEventListener('keyup', this.handleOwn.bind(this, true)); | |
}, | |
handleOwn: function (request, e) { | |
var target = e.target; | |
if (this._ignore[target.tagName.toLowerCase()]) | |
return this; | |
var key = this._keymap[e.which || e.keyCode]; | |
if (!key) | |
return this; | |
var modifiers = this._modifiers; | |
if (e.altKey) | |
key |= modifiers.alt; | |
if (e.metaKey) | |
key |= modifiers.meta; | |
if (e.ctrlKey) | |
key |= modifiers.ctrl; | |
if (e.shiftKey) | |
key |= modifiers.shift; | |
var binding = this._bindings[key]; | |
if (!binding) | |
return this; | |
e.preventDefault(); | |
e.stopPropagation(); | |
if (request) | |
SpotifyApi.api.request('keyboard_trigger_binding', [binding], this, this._empty, this._empty); | |
} | |
}; | |
kbd.init(); | |
}()); | |
}, | |
'node_modules/quickstart-spotify/api-core.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
if (!window.SpotifyApi) { | |
window.SpotifyApi = require('node_modules/api/scripts/core.js'); | |
if (window._getSpotifyModule) | |
require('node_modules/api/scripts/core.desktop.js'); | |
else | |
require('node_modules/api/scripts/core.browser.js'); | |
} | |
window.SP = window.SpotifyApi.api; | |
}, | |
'scripts/player.events.js': function (require, module, exports, global, __filename, __dirname) { | |
function EventManager() { | |
this.listeners = {}; | |
} | |
EventManager.prototype.subscribe = function (event, callback, context) { | |
if (this.listeners[event] === undefined) { | |
this.listeners[event] = []; | |
} | |
this.listeners[event].push([ | |
callback, | |
context || null | |
]); | |
}; | |
EventManager.prototype.unsubscribe = function (event, callback) { | |
if (this.listeners[event] === undefined) { | |
return; | |
} | |
var count = this.listeners[event].length; | |
for (var i = 0; i < count; i++) { | |
if (this.listeners[event][i][0] === callback) { | |
this.listeners[event].splice(i, 1); | |
break; | |
} | |
} | |
}; | |
EventManager.prototype.trigger = function (event, data) { | |
if (this.listeners[event] === undefined) { | |
return; | |
} | |
var count = this.listeners[event].length; | |
for (var i = 0; i < count; i++) { | |
var func = this.listeners[event][i][0]; | |
var context = this.listeners[event][i][1] || this; | |
func.call(context, data || {}); | |
} | |
}; | |
EventManager.prototype.Events = function () { | |
return { | |
TRACK_CHANGED: 'TRACK_CHANGED', | |
AD_CLICKED: 'AD_CLICKED', | |
TRACK_SKIPPED: 'TRACK_SKIPPED', | |
BACK_BUTTON_PRESS_START: 'BACK_BUTTON_PRESS_START', | |
BACK_BUTTON_PRESS_COMPLETE: 'BACK_BUTTON_PRESS_COMPLETE' | |
}; | |
}(); | |
exports.EventManager = EventManager; | |
}, | |
'scripts/player-utils.js': function (require, module, exports, global, __filename, __dirname) { | |
function trackHistory(length) { | |
this._maxLength = length; | |
this._items = new Array(); | |
} | |
trackHistory.prototype.add = function (item) { | |
var oldItems = this._items.slice(0); | |
for (var i = 0; i < oldItems.length; i++) { | |
if (oldItems[i].uri === item.uri) { | |
return false; | |
} | |
} | |
this._items[0] = item; | |
for (var i = 1; i < this._maxLength; i++) { | |
if (typeof oldItems[i - 1] !== 'undefined') { | |
this._items[i] = oldItems[i - 1]; | |
} | |
} | |
return this._items; | |
}; | |
trackHistory.prototype.show = function (field) { | |
if (!field || typeof this.get(0)[field] === 'undefined') { | |
return this._items; | |
} | |
var output = []; | |
for (var i = 0; i < this._items.length; i++) { | |
output[i] = this._items[i][field]; | |
} | |
return output; | |
}; | |
trackHistory.prototype.get = function (i) { | |
return this._items[i]; | |
}; | |
trackHistory.prototype.size = function () { | |
return this._items.length; | |
}; | |
trackHistory.prototype.forEach = function (cb) { | |
for (var i = 0; i < this._items.length; i++) { | |
cb(this._items[i]); | |
} | |
}; | |
function playerStorage() { | |
try { | |
this.store = typeof window.localStorage != 'undefined' ? window.localStorage : false; | |
} catch (e) { | |
this.store = false; | |
} | |
} | |
playerStorage.prototype.set = function (item, val) { | |
if (!this.store) { | |
return false; | |
} | |
try { | |
this.store.setItem(item, val); | |
} catch (e) { | |
return false; | |
} | |
}; | |
playerStorage.prototype.get = function (item) { | |
if (!this.store) { | |
return false; | |
} | |
return this.store.getItem(item); | |
}; | |
var Konami = function () { | |
'use strict'; | |
var _CODE = [ | |
38, | |
38, | |
40, | |
40, | |
37, | |
39, | |
37, | |
39, | |
66, | |
65, | |
13 | |
], _CODE_LEN = _CODE.length, _listenerTarget = null, _onSuccess = null, next = 0, _keydown_listener = function (e) { | |
if (e.keyCode === _CODE[next]) { | |
next += 1; | |
if (next === _CODE_LEN) { | |
_onSuccess(); | |
next = 0; | |
} | |
} else { | |
next = 0; | |
} | |
}, _addEventListeners = function () { | |
if (_listenerTarget.addEventListener) { | |
_listenerTarget.addEventListener('keydown', _keydown_listener, false); | |
} else if (_listenerTarget.attachEvent) { | |
_listenerTarget.attachEvent('onkeydown', _keydown_listener); | |
} else { | |
if (typeof _listenerTarget.onkeydown === 'function') { | |
var preservedListenerTargetFunction = _listenerTarget.onkeydown; | |
_listenerTarget.onkeydown = function (e) { | |
preservedListenerTargetFunction(e); | |
_keydown_listener(e); | |
}; | |
} else { | |
_listenerTarget.onkeydown = _keydown_listener; | |
} | |
} | |
}; | |
return { | |
onSuccess: function () { | |
}, | |
listenerTarget: window, | |
init: function () { | |
_onSuccess = this.onSuccess; | |
_listenerTarget = this.listenerTarget; | |
_addEventListeners(); | |
} | |
}; | |
}; | |
var playerUtils = { | |
hasClassList: 'classList' in document.createElement('a'), | |
secsToMins: function (secs, keepMs) { | |
var secsRounded = Math.floor(secs); | |
var mins = Math.floor(secsRounded / 60) + ':' + ('0' + secsRounded % 60).slice(-2); | |
if (undefined !== keepMs && keepMs) { | |
mins += '.' + Math.round((secs - secsRounded) * 1000); | |
} | |
return mins; | |
}, | |
getId: function (id) { | |
return document.getElementById(id); | |
}, | |
hasClass: function (ele, cls) { | |
if (this.hasClassList) { | |
return ele.classList.contains(cls); | |
} | |
return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); | |
}, | |
addClass: function (ele, cls) { | |
if (!this.hasClass(ele, cls)) { | |
if (this.hasClassList) { | |
ele.classList.add(cls); | |
} else { | |
ele.className += ' ' + cls; | |
} | |
} | |
}, | |
removeClass: function (ele, cls) { | |
if (this.hasClass(ele, cls)) { | |
if (this.hasClassList) { | |
ele.classList.remove(cls); | |
} else { | |
var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); | |
ele.className = ele.className.replace(reg, ' '); | |
} | |
} | |
}, | |
addEventSimple: function (obj, evt, fn) { | |
if (obj.addEventListener) | |
obj.addEventListener(evt, fn, false); | |
else if (obj.attachEvent) | |
obj.attachEvent('on' + evt, fn); | |
}, | |
removeEventSimple: function (obj, evt, fn) { | |
if (obj.removeEventListener) | |
obj.removeEventListener(evt, fn, false); | |
else if (obj.detachEvent) | |
obj.detachEvent('on' + evt, fn); | |
}, | |
extendObject: function (destination, source) { | |
for (var property in source) { | |
if (source[property] && source[property].constructor && source[property].constructor === Object) { | |
destination[property] = destination[property] || {}; | |
arguments.callee(destination[property], source[property]); | |
} else { | |
destination[property] = source[property]; | |
} | |
} | |
return destination; | |
}, | |
proxy: function (fn, context) { | |
return function () { | |
return fn.apply(context, arguments); | |
}; | |
}, | |
appNameFromUri: function (uri) { | |
var parts = uri.split(':'); | |
var appName = parts[1]; | |
if (appName === 'app') { | |
appName = parts[2]; | |
} else if (appName === 'user' && parts[3] === 'playlist') { | |
appName = 'playlist'; | |
} | |
return appName; | |
} | |
}; | |
function Marquee(element, maxWidth) { | |
this.utils = playerUtils; | |
this.element = element; | |
this.maxWidth = maxWidth; | |
this.timer = null; | |
this.left = 0; | |
this.hovered = false; | |
this.scrolling = false; | |
this.shouldScroll = false; | |
} | |
Marquee.prototype.init = function () { | |
this.onMouseOver = this.onMouseOver.bind(this); | |
this.onMouseOut = this.onMouseOut.bind(this); | |
this.scroll = this.scroll.bind(this); | |
this.utils.addEventSimple(this.element, 'mouseover', this.onMouseOver); | |
this.utils.addEventSimple(this.element, 'mouseout', this.onMouseOut); | |
this.element.style.position = 'relative'; | |
this.titlePadderWidth = 40; | |
this.titlePadder = '<span style="padding-left:' + this.titlePadderWidth + 'px;"></span>'; | |
}; | |
Marquee.prototype.refresh = function () { | |
if (this.scrolling) { | |
this.stop(); | |
} | |
this.textWidth = this.element.offsetWidth; | |
this.shouldScroll = this.textWidth > this.maxWidth; | |
if (this.shouldScroll) { | |
this.element.style.left = '0px'; | |
this.left = 0; | |
} else { | |
this.element.style.left = (this.maxWidth - this.element.offsetWidth) / 2 + 'px'; | |
} | |
}; | |
Marquee.prototype.widthAdjust = function (width) { | |
this.maxWidth = width; | |
this.refresh(); | |
}; | |
Marquee.prototype.stop = function () { | |
if (!this.timer) { | |
return; | |
} | |
clearInterval(this.timer); | |
this.element.innerHTML = this._originalText; | |
this.scrolling = false; | |
this.refresh(); | |
}; | |
Marquee.prototype.scroll = function () { | |
if (Math.abs(this.left) === this.textWidth + this.titlePadderWidth) { | |
this.element.style.left = '0px'; | |
this.left = 0; | |
if (!this.hovered) { | |
this.stop(); | |
} | |
return; | |
} | |
this.left--; | |
this.element.style.left = '' + this.left + 'px'; | |
}; | |
Marquee.prototype.onMouseOver = function (e) { | |
if (this.shouldScroll && !this.scrolling) { | |
this.scrolling = true; | |
this._originalText = this.element.innerHTML; | |
this.element.innerHTML = this._originalText + this.titlePadder + this._originalText; | |
this.timer = setInterval(this.scroll, 15); | |
} | |
}; | |
Marquee.prototype.onMouseOut = function (e) { | |
this.hovered = false; | |
}; | |
exports.trackHistory = trackHistory; | |
exports.playerStorage = playerStorage; | |
exports.playerUtils = playerUtils; | |
exports.Marquee = Marquee; | |
exports.kc = Konami; | |
}, | |
'supported-languages.json': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = [ | |
'de', | |
'en', | |
'el', | |
'es', | |
'es-419', | |
'fi', | |
'fr', | |
'fr-ca', | |
'id', | |
'it', | |
'ja', | |
'nl', | |
'pl', | |
'pt-br', | |
'ru', | |
'sv', | |
'th', | |
'tr', | |
'zsm', | |
'zh-hant' | |
]; | |
}, | |
'de.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('de.loc/strings/main.lang', { | |
'play': 'Play', | |
'pause': 'Pause', | |
'next': 'Weiter', | |
'back': 'Zur\xFCck', | |
'add-collection': 'Zur Sammlung hinzuf\xFCgen', | |
'added-collection': 'Zur Sammlung hinzugef\xFCgt', | |
'remove-collection': 'Aus der Sammlung entfernen', | |
'removed-collection': 'Von der Sammlung entfernt', | |
'shuffle': 'Shuffle', | |
'repeat': 'Wiederholen', | |
'start-radio-suggestion': 'Klick unten, um eine Radiostation, basierend auf diesen Empfehlungen, zu erstellen.' | |
}); | |
}, | |
'en.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('en.loc/strings/main.lang', { | |
'play': 'Play', | |
'pause': 'Pause', | |
'next': 'Next', | |
'back': 'Back', | |
'add-collection': 'Add to collection', | |
'added-collection': 'Added to collection', | |
'remove-collection': 'Remove from collection', | |
'removed-collection': 'Removed from collection', | |
'shuffle': 'Shuffle', | |
'repeat': 'Repeat', | |
'start-radio-suggestion': 'Click below to start a radio station from one of these recommended tracks.' | |
}); | |
}, | |
'el.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('el.loc/strings/main.lang', { | |
'play': 'Play', | |
'pause': '\u03A0\u03B1\u03CD\u03C3\u03B7', | |
'next': '\u0395\u03C0\u03CC\u03BC\u03B5\u03BD\u03BF', | |
'back': '\u03A0\u03AF\u03C3\u03C9', | |
'add-collection': '\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C3\u03C4\u03B7 \u03C3\u03C5\u03BB\u03BB\u03BF\u03B3\u03AE', | |
'added-collection': '\u03A0\u03C1\u03BF\u03C3\u03C4\u03AD\u03B8\u03B7\u03BA\u03B5 \u03C3\u03C4\u03B7 \u03C3\u03C5\u03BB\u03BB\u03BF\u03B3\u03AE', | |
'remove-collection': '\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03B1\u03C0\u03CC \u03C4\u03B7 \u03C3\u03C5\u03BB\u03BB\u03BF\u03B3\u03AE', | |
'removed-collection': '\u0391\u03C6\u03B1\u03B9\u03C1\u03AD\u03B8\u03B7\u03BA\u03B5 \u03B1\u03C0\u03CC \u03C4\u03B7 \u03C3\u03C5\u03BB\u03BB\u03BF\u03B3\u03AE', | |
'shuffle': '\u03A4\u03C5\u03C7\u03B1\u03AF\u03B1 \u03C3\u03B5\u03B9\u03C1\u03AC', | |
'repeat': '\u0395\u03C0\u03B1\u03BD\u03AC\u03BB\u03B7\u03C8\u03B7', | |
'start-radio-suggestion': '\u039A\u03AC\u03BD\u03B5 \u03BA\u03BB\u03B9\u03BA \u03C0\u03B1\u03C1\u03B1\u03BA\u03AC\u03C4\u03C9 \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03BE\u03B5\u03BA\u03B9\u03BD\u03AE\u03C3\u03B5\u03B9\u03C2 \u03AD\u03BD\u03B1 \u03C1\u03B1\u03B4\u03B9\u03BF\u03C6\u03C9\u03BD\u03B9\u03BA\u03CC \u03C3\u03C4\u03B1\u03B8\u03BC\u03CC \u03BC\u03B5 \u03AD\u03BD\u03B1 \u03B1\u03C0\u03CC \u03B1\u03C5\u03C4\u03AC \u03C4\u03B1 \u03C0\u03C1\u03BF\u03C4\u03B5\u03B9\u03BD\u03CC\u03BC\u03B5\u03BD\u03B1 \u03BA\u03BF\u03BC\u03BC\u03AC\u03C4\u03B9\u03B1.' | |
}); | |
}, | |
'es-419.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('es-419.loc/strings/main.lang', { | |
'play': 'Reproducir', | |
'pause': 'Pausa', | |
'next': 'Siguiente', | |
'back': 'Atr\xE1s', | |
'add-collection': 'Agregar a la colecci\xF3n', | |
'added-collection': 'Agregada a colecci\xF3n', | |
'remove-collection': 'Retirar de la colecci\xF3n', | |
'removed-collection': 'Eliminada de colecci\xF3n', | |
'shuffle': 'Aleatorio', | |
'repeat': 'Repetir', | |
'start-radio-suggestion': 'Haz clic para iniciar una estaci\xF3n de radio con una de estas canciones recomendadas.' | |
}); | |
}, | |
'fi.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('fi.loc/strings/main.lang', { | |
'play': 'Toista', | |
'pause': 'Tauko', | |
'next': 'Seuraava', | |
'back': 'Takaisin', | |
'add-collection': 'Lis\xE4\xE4 kokoelmaan', | |
'added-collection': 'Lis\xE4tty kokoelmaan', | |
'remove-collection': 'Poista kokoelmasta', | |
'removed-collection': 'Poistettu kokoelmasta', | |
'shuffle': 'Satunnaistoisto', | |
'repeat': 'Toista', | |
'start-radio-suggestion': 'K\xE4ynnist\xE4 radioasema jostakin n\xE4ist\xE4 suositelluista kappaleista napsauttamalla alla olevaa linkki\xE4.' | |
}); | |
}, | |
'es.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('es.loc/strings/main.lang', { | |
'play': 'Reproducir', | |
'pause': 'Pausa', | |
'next': 'Siguiente', | |
'back': 'Atr\xE1s', | |
'add-collection': 'A\xF1adir a la colecci\xF3n', | |
'added-collection': 'A\xF1adido/a a la colecci\xF3n', | |
'remove-collection': 'Retirar de la colecci\xF3n', | |
'removed-collection': 'Retirado/a de la colecci\xF3n', | |
'shuffle': 'Aleatoria', | |
'repeat': 'Repetir', | |
'start-radio-suggestion': 'Haz click a continuaci\xF3n para iniciar una emisora de radio a partir de una de estas canciones recomendadas.' | |
}); | |
}, | |
'fr.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('fr.loc/strings/main.lang', { | |
'play': 'Lire', | |
'pause': 'Pause', | |
'next': 'Suivant', | |
'back': 'Retour', | |
'add-collection': 'Ajouter \xE0 la collection', | |
'added-collection': 'Ajout\xE9 \xE0 la collection', | |
'remove-collection': 'Supprimer de la collection', | |
'removed-collection': 'Supprim\xE9 de la collection', | |
'shuffle': 'Lecture al\xE9atoire', | |
'repeat': 'R\xE9p\xE9ter', | |
'start-radio-suggestion': 'Cliquez ci-dessous pour lancer la radio \xE0 partir de l\'un de ces titres recommand\xE9s.' | |
}); | |
}, | |
'fr-ca.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('fr-ca.loc/strings/main.lang', { | |
'play': 'Lire', | |
'pause': 'Pause', | |
'next': 'Suivant', | |
'back': 'Retour', | |
'add-collection': 'Ajouter \xE0 la collection', | |
'added-collection': 'Ajout\xE9 \xE0 la collection', | |
'remove-collection': 'Supprimer de la collection', | |
'removed-collection': 'Supprim\xE9 de la collection', | |
'shuffle': 'Lecture al\xE9atoire', | |
'repeat': 'R\xE9p\xE9ter', | |
'start-radio-suggestion': 'Cliquez ci-dessous pour lancer la station de radio \xE0 partir de l\'une de ces pistes recommand\xE9es.' | |
}); | |
}, | |
'id.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('id.loc/strings/main.lang', { | |
'play': 'Putar', | |
'pause': 'Jeda', | |
'next': 'Berikutnya', | |
'back': 'Kembali', | |
'add-collection': 'Tambahkan ke koleksi', | |
'added-collection': 'Ditambahkan ke koleksi', | |
'remove-collection': 'Hapus dari koleksi', | |
'removed-collection': 'Dihapus dari koleksi', | |
'shuffle': 'Acak', | |
'repeat': 'Ulangi', | |
'start-radio-suggestion': 'Klik di bawah ini untuk memulai stasiun radio dari salah satu lagu yang disarankan ini.' | |
}); | |
}, | |
'ja.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('ja.loc/strings/main.lang', { | |
'play': '\u518D\u751F', | |
'pause': '\u4E00\u6642\u505C\u6B62', | |
'next': '\u6B21\u3078', | |
'back': '\u623B\u308B', | |
'add-collection': '\u30B3\u30EC\u30AF\u30B7\u30E7\u30F3\u306B\u8FFD\u52A0', | |
'added-collection': '\u30B3\u30EC\u30AF\u30B7\u30E7\u30F3\u306B\u8FFD\u52A0\u6E08\u307F', | |
'remove-collection': '\u30B3\u30EC\u30AF\u30B7\u30E7\u30F3\u304B\u3089\u524A\u9664', | |
'removed-collection': '\u30B3\u30EC\u30AF\u30B7\u30E7\u30F3\u304B\u3089\u524A\u9664\u6E08\u307F', | |
'shuffle': '\u30B7\u30E3\u30C3\u30D5\u30EB', | |
'repeat': '\u7E70\u308A\u8FD4\u3057', | |
'start-radio-suggestion': '\u3053\u308C\u3089\u3044\u305A\u308C\u304B\u306E\u304A\u3059\u3059\u3081\u30C8\u30E9\u30C3\u30AF\u304B\u3089\u30B9\u30C6\u30FC\u30B7\u30E7\u30F3\u3092\u958B\u59CB\u3059\u308B\u306B\u306F\u4EE5\u4E0B\u3092\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u304F\u3060\u3055\u3044\u3002' | |
}); | |
}, | |
'it.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('it.loc/strings/main.lang', { | |
'play': 'Play', | |
'pause': 'Pausa', | |
'next': 'Avanti', | |
'back': 'Indietro', | |
'add-collection': 'Aggiungi alla libreria', | |
'added-collection': 'Aggiunto alla libreria', | |
'remove-collection': 'Rimuovi dalla libreria', | |
'removed-collection': 'Rimosso dalla libreria', | |
'shuffle': 'Shuffle', | |
'repeat': 'Ripeti', | |
'start-radio-suggestion': 'Clicca sotto per avviare una stazione radio partendo da uno di questi brani consigliati.' | |
}); | |
}, | |
'nl.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('nl.loc/strings/main.lang', { | |
'play': 'Afspelen', | |
'pause': 'Pauze', | |
'next': 'Volgende', | |
'back': 'Terug', | |
'add-collection': 'Toevoegen aan collectie', | |
'added-collection': 'Toegevoegd aan collectie', | |
'remove-collection': 'Verwijderen uit collectie', | |
'removed-collection': 'Verwijderd uit collectie', | |
'shuffle': 'Shuffle', | |
'repeat': 'Herhalen', | |
'start-radio-suggestion': 'Klik hieronder om een radiostation te starten vanuit een van deze aanbevolen nummers.' | |
}); | |
}, | |
'pl.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('pl.loc/strings/main.lang', { | |
'play': 'Odtwarzaj', | |
'pause': 'Pauza', | |
'next': 'Dalej', | |
'back': 'Powr\xF3t', | |
'add-collection': 'Dodaj do kolekcji', | |
'added-collection': 'Dodano do kolekcji', | |
'remove-collection': 'Usu\u0144 z kolekcji', | |
'removed-collection': 'Usuni\u0119to z kolekcji', | |
'shuffle': 'Losowo', | |
'repeat': 'Powt\xF3rz', | |
'start-radio-suggestion': 'Kliknij poni\u017Cej, aby w\u0142\u0105czy\u0107 stacj\u0119 radiow\u0105 dla jednej z tych rekomendowanych utwor\xF3w.' | |
}); | |
}, | |
'pt-br.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('pt-br.loc/strings/main.lang', { | |
'play': 'Play', | |
'pause': 'Pausar', | |
'next': 'Avan\xE7ar', | |
'back': 'Voltar', | |
'add-collection': 'Adicionar \xE0 cole\xE7\xE3o', | |
'added-collection': 'Adicionado \xE0 cole\xE7\xE3o', | |
'remove-collection': 'Tirar da cole\xE7\xE3o', | |
'removed-collection': 'Tirada da cole\xE7\xE3o', | |
'shuffle': 'Tocar em ordem aleat\xF3ria', | |
'repeat': 'Repetir', | |
'start-radio-suggestion': 'Clique abaixo para iniciar uma esta\xE7\xE3o de r\xE1dio com uma destas faixas recomendadas.' | |
}); | |
}, | |
'ru.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('ru.loc/strings/main.lang', { | |
'play': '\u0412\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0435\u0441\u0442\u0438', | |
'pause': '\u041F\u0430\u0443\u0437\u0430', | |
'next': '\u0414\u0430\u043B\u0435\u0435', | |
'back': '\u041D\u0430\u0437\u0430\u0434', | |
'add-collection': '\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432 \u043A\u043E\u043B\u043B\u0435\u043A\u0446\u0438\u044E', | |
'added-collection': '\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u043E \u0432 \u043A\u043E\u043B\u043B\u0435\u043A\u0446\u0438\u044E', | |
'remove-collection': '\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0438\u0437 \u043A\u043E\u043B\u043B\u0435\u043A\u0446\u0438\u0438', | |
'removed-collection': '\u0423\u0434\u0430\u043B\u0435\u043D\u043E \u0438\u0437 \u043A\u043E\u043B\u043B\u0435\u043A\u0446\u0438\u0438', | |
'shuffle': '\u041F\u0435\u0440\u0435\u043C\u0435\u0448\u0430\u0442\u044C', | |
'repeat': '\u041F\u043E\u0432\u0442\u043E\u0440', | |
'start-radio-suggestion': '\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043D\u0430 \u043B\u044E\u0431\u043E\u0439 \u043F\u0443\u043D\u043A\u0442 \u0432 \u0441\u043F\u0438\u0441\u043A\u0435, \u0447\u0442\u043E\u0431\u044B \u0432\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0440\u0430\u0434\u0438\u043E\u0441\u0442\u0430\u043D\u0446\u0438\u044E \u0438 \u043D\u0430\u0447\u0430\u0442\u044C \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0435\u0434\u0435\u043D\u0438\u0435 \u0441 \u043E\u0434\u043D\u043E\u0433\u043E \u0438\u0437 \u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u043E\u0432\u0430\u043D\u043D\u044B\u0445 \u0442\u0440\u0435\u043A\u043E\u0432.' | |
}); | |
}, | |
'sv.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('sv.loc/strings/main.lang', { | |
'play': 'Spela upp', | |
'pause': 'Pausa', | |
'next': 'N\xE4sta', | |
'back': 'Bak\xE5t', | |
'add-collection': 'L\xE4gg till i samling', | |
'added-collection': 'Lades till i samlingen', | |
'remove-collection': 'Ta bort fr\xE5n samling', | |
'removed-collection': 'Togs bort fr\xE5n samlingen', | |
'shuffle': 'Shuffle', | |
'repeat': 'Repetera', | |
'start-radio-suggestion': 'Klicka nedan om du vill starta en radiostation fr\xE5n n\xE5got av dessa rekommenderade sp\xE5r.' | |
}); | |
}, | |
'th.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('th.loc/strings/main.lang', { | |
'play': '\u0E40\u0E25\u0E48\u0E19', | |
'pause': '\u0E2B\u0E22\u0E38\u0E14\u0E0A\u0E31\u0E48\u0E27\u0E04\u0E23\u0E32\u0E27', | |
'next': '\u0E16\u0E31\u0E14\u0E44\u0E1B', | |
'back': '\u0E01\u0E25\u0E31\u0E1A', | |
'add-collection': '\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E44\u0E1B\u0E17\u0E35\u0E48\u0E04\u0E2D\u0E25\u0E40\u0E25\u0E04\u0E0A\u0E31\u0E19', | |
'added-collection': '\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E44\u0E1B\u0E17\u0E35\u0E48\u0E04\u0E2D\u0E25\u0E40\u0E25\u0E04\u0E0A\u0E31\u0E19\u0E41\u0E25\u0E49\u0E27', | |
'remove-collection': '\u0E25\u0E1A\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E04\u0E2D\u0E25\u0E40\u0E25\u0E04\u0E0A\u0E31\u0E19', | |
'removed-collection': '\u0E25\u0E1A\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E04\u0E2D\u0E25\u0E40\u0E25\u0E04\u0E0A\u0E31\u0E19\u0E41\u0E25\u0E49\u0E27', | |
'shuffle': '\u0E2A\u0E38\u0E48\u0E21', | |
'repeat': '\u0E0B\u0E49\u0E33', | |
'start-radio-suggestion': '\u0E04\u0E25\u0E34\u0E01\u0E14\u0E49\u0E32\u0E19\u0E25\u0E48\u0E32\u0E07\u0E40\u0E1E\u0E37\u0E48\u0E2D\u0E40\u0E23\u0E34\u0E48\u0E21\u0E2A\u0E16\u0E32\u0E19\u0E35\u0E27\u0E34\u0E17\u0E22\u0E38\u0E08\u0E32\u0E01\u0E2B\u0E19\u0E36\u0E48\u0E07\u0E43\u0E19\u0E41\u0E17\u0E23\u0E47\u0E01\u0E17\u0E35\u0E48\u0E41\u0E19\u0E30\u0E19\u0E33\u0E40\u0E2B\u0E25\u0E48\u0E32\u0E19\u0E35\u0E49' | |
}); | |
}, | |
'tr.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('tr.loc/strings/main.lang', { | |
'play': '\xC7al', | |
'pause': 'Duraklat', | |
'next': 'Sonraki', | |
'back': 'Geri', | |
'add-collection': 'Koleksiyona ekle', | |
'added-collection': 'Koleksiyona eklendi', | |
'remove-collection': 'Koleksiyondan kald\u0131r', | |
'removed-collection': 'Koleksiyondan \xE7\u0131kar\u0131ld\u0131', | |
'shuffle': 'Kar\u0131\u015F\u0131k \xC7al', | |
'repeat': 'Tekrarla', | |
'start-radio-suggestion': 'Bu \xF6nerilen par\xE7alar\u0131n birinden bir radyo istasyonu olu\u015Fturmak i\xE7in a\u015Fa\u011F\u0131ya t\u0131kla.' | |
}); | |
}, | |
'zsm.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('zsm.loc/strings/main.lang', { | |
'play': 'Main', | |
'pause': 'Jeda', | |
'next': 'Seterusnya', | |
'back': 'Kembali', | |
'add-collection': 'Tambah ke koleksi', | |
'added-collection': 'Ditambah ke koleksi', | |
'remove-collection': 'Keluarkan dari koleksi', | |
'removed-collection': 'Dikeluarkan dari koleksi', | |
'shuffle': 'Shuffle', | |
'repeat': 'Ulang', | |
'start-radio-suggestion': 'Klik di bawah untuk memulakan stesen radio dari salah satu lagu dicadangkan.' | |
}); | |
}, | |
'zh-hant.loc/strings/main.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('zh-hant.loc/strings/main.lang', { | |
'play': '\u64AD\u653E', | |
'pause': '\u66AB\u505C', | |
'next': '\u4E0B\u4E00\u6B65', | |
'back': '\u8FD4\u56DE', | |
'add-collection': '\u65B0\u589E\u81F3\u6536\u85CF', | |
'added-collection': '\u5DF2\u65B0\u589E\u81F3\u6536\u85CF', | |
'remove-collection': '\u5F9E\u6536\u85CF\u4E2D\u79FB\u9664', | |
'removed-collection': '\u5F9E\u6536\u85CF\u4E2D\u79FB\u9664', | |
'shuffle': '\u96A8\u6A5F\u64AD\u653E', | |
'repeat': '\u91CD\u8907', | |
'start-radio-suggestion': '\u6309\u4E0B\u65B9\u7684\u63A8\u85A6\u6B4C\u66F2\u958B\u555F\u96FB\u53F0\u983B\u9053\u3002' | |
}); | |
}, | |
'scripts/utils.draggable.js': function (require, module, exports, global, __filename, __dirname) { | |
function Draggable(element, userOpts) { | |
var initialMouseX = undefined, initialMouseY = undefined, startX = undefined, startY = undefined, draggedObject = undefined, container = false, opts = { | |
constrainDir: false, | |
constrainParent: false, | |
onStart: function () { | |
}, | |
onMove: function () { | |
}, | |
onComplete: function () { | |
} | |
}; | |
self.element = element; | |
if (typeof element === 'string') { | |
self.element = document.getElementById(element); | |
} | |
element.onmousedown = function (e) { | |
startDragMouse(e); | |
}; | |
for (var property in userOpts) { | |
opts[property] = userOpts[property]; | |
} | |
this.setContainerSize = function () { | |
container = setContainer(element.offsetParent); | |
}; | |
if (opts.constrainParent) { | |
this.setContainerSize(); | |
} | |
function setContainer(ele) { | |
return { | |
maxW: ele.offsetWidth, | |
maxH: ele.offsetHeight | |
}; | |
} | |
var startDragMouse = function (e) { | |
var doStart = opts.onStart(element); | |
if (doStart === false) { | |
return false; | |
} | |
startDrag(element); | |
var evt = e || window.event; | |
evt.preventDefault(); | |
initialMouseX = evt.clientX; | |
initialMouseY = evt.clientY; | |
document.addEventListener('mousemove', dragMouse); | |
document.addEventListener('mouseup', releaseElement); | |
document.addEventListener('mouseout', mouseOutWindow); | |
return false; | |
}; | |
var startDrag = function (obj) { | |
if (draggedObject) { | |
releaseElement(); | |
} | |
startX = obj.offsetLeft; | |
startY = obj.offsetTop; | |
draggedObject = obj; | |
}; | |
var dragMouse = function (e) { | |
var evt = e || window.event; | |
evt.preventDefault(); | |
var dX = evt.clientX - initialMouseX; | |
var dY = evt.clientY - initialMouseY; | |
setPosition(dX, dY); | |
return false; | |
}; | |
var setPosition = function (dx, dy) { | |
var posX = startX + dx, posY = startY + dy; | |
if (opts.constrainDir !== 'y') { | |
if (container) { | |
if (posX < 0) { | |
posX = 0; | |
} | |
if (posX > container.maxW - draggedObject.offsetWidth) { | |
posX = container.maxW - draggedObject.offsetWidth; | |
} | |
} | |
draggedObject.style.left = posX + 'px'; | |
} | |
if (opts.constrainDir !== 'x') { | |
if (container) { | |
if (posY < 0) { | |
posY = 0; | |
} | |
if (posY > container.maxH - draggedObject.offsetHeight) { | |
posY = container.maxH - draggedObject.offsetHeight; | |
} | |
} | |
draggedObject.style.top = posY + 'px'; | |
} | |
opts.onMove(draggedObject, posX, posY); | |
}; | |
var mouseOutWindow = function (e) { | |
e = e ? e : window.event; | |
var from = e.relatedTarget || e.toElement; | |
if (!from || from.nodeName == 'HTML') { | |
releaseElement(); | |
} | |
}; | |
var releaseElement = function () { | |
document.removeEventListener('mousemove', dragMouse); | |
document.removeEventListener('mouseup', releaseElement); | |
document.removeEventListener('mouseout', mouseOutWindow); | |
opts.onComplete(draggedObject); | |
draggedObject = null; | |
}; | |
this.startDrag = startDragMouse; | |
} | |
exports.Draggable = Draggable; | |
}, | |
'scripts/utils.slider.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (Draggable) { | |
function Slider(element, handle, userOpts) { | |
var self = this; | |
self._opts = { | |
steps: 100, | |
start: 0, | |
onStart: function () { | |
}, | |
onChange: function () { | |
}, | |
onComplete: function () { | |
} | |
}; | |
self._disabled = false; | |
self._element = element; | |
self._handle = handle; | |
if (typeof element === 'string') { | |
} | |
if (typeof handle === 'string') { | |
self._handle = document.getElementById(handle); | |
} | |
self.step = self._opts.start; | |
self.totalPx = element.offsetWidth - self._handle.offsetWidth; | |
for (var property in userOpts) { | |
self._opts[property] = userOpts[property]; | |
} | |
self._moveHandle = function () { | |
self._handle.style.left = self.step / self._opts.steps * self.totalPx + 'px'; | |
}; | |
var _calculateStep = function (posX) { | |
self.step = Math.round(posX / self.totalPx * self._opts.steps); | |
self._opts.onChange.apply(self); | |
}; | |
var _startDragHandle = function (element) { | |
if (self._disabled) { | |
return false; | |
} | |
element.className = 'dragging'; | |
self._opts.onStart.apply(self); | |
}; | |
var _onHandleMove = function (handle, posX, posY) { | |
_calculateStep(posX); | |
}; | |
var _endDragHandle = function (handle) { | |
self._handle.className = ''; | |
self._opts.onComplete.apply(self); | |
}; | |
this._handleClick = function (e, opt_pos) { | |
var self = this; | |
if (self._disabled || e.target === handle) { | |
return false; | |
} | |
e.stopPropagation(); | |
var posX = opt_pos || e.offsetX || e.layerX; | |
posX = posX - self._handle.offsetWidth / 2; | |
posX = posX < 0 ? 0 : posX > self.totalPx ? self.totalPx : posX; | |
_calculateStep(posX); | |
self._moveHandle(); | |
self._opts.onChange.apply(self); | |
self._knob.startDrag(e); | |
}; | |
this._knob = new Draggable(handle, { | |
constrainParent: true, | |
constrainDir: 'x', | |
onStart: _startDragHandle, | |
onMove: _onHandleMove, | |
onComplete: _endDragHandle | |
}); | |
element.addEventListener('mousedown', function (e) { | |
self._handleClick(e); | |
}, false); | |
self.handle = self._handle; | |
self.jumpToStep(self._opts.start, false); | |
} | |
Slider.prototype.jumpToStep = function (step, doComplete) { | |
if (self._disabled) { | |
return false; | |
} | |
this.step = step; | |
this._opts.onChange.apply(this); | |
this._moveHandle(); | |
if (doComplete !== false) { | |
this._opts.onComplete.apply(this); | |
} | |
}; | |
Slider.prototype.handleResize = function () { | |
this.totalPx = this._element.offsetWidth - this._handle.offsetWidth; | |
this._moveHandle(); | |
this._knob.setContainerSize(); | |
this._opts.onChange.apply(this); | |
}; | |
Slider.prototype.setSteps = function (steps) { | |
this._opts.steps = steps; | |
}; | |
Slider.prototype.disable = function () { | |
this._disabled = true; | |
}; | |
Slider.prototype.enable = function () { | |
this._disabled = false; | |
}; | |
Slider.prototype.handleClick = function (e, opt_pos) { | |
this._handleClick(e, opt_pos); | |
}; | |
exports.Slider = Slider; | |
}(require('scripts/utils.draggable.js').Draggable)); | |
}, | |
'node_modules/api/supported-languages.json': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = [ | |
'arb', | |
'de', | |
'el', | |
'en', | |
'es', | |
'es-la', | |
'es-419', | |
'fi', | |
'fr', | |
'hu', | |
'id', | |
'it', | |
'ja', | |
'nl', | |
'pl', | |
'pt-br', | |
'ro', | |
'ru', | |
'sv', | |
'th', | |
'tr', | |
'zh-hant', | |
'zsm' | |
]; | |
}, | |
'node_modules/spotify-events/util/type.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var FOLLOW = 'follow'; | |
var COLLECTION = 'collection'; | |
var UNKNOWN = 'unknown'; | |
module.exports = function (uri) { | |
if (/^spotify:user:(.*):playlist:/.test(uri)) { | |
return FOLLOW; | |
} else if (/^spotify:user:/.test(uri)) { | |
return FOLLOW; | |
} else if (/^spotify:artist:/.test(uri)) { | |
return FOLLOW; | |
} else if (/^spotify:album:/.test(uri)) { | |
return COLLECTION; | |
} else if (/^spotify:track:/.test(uri)) { | |
return COLLECTION; | |
} | |
return UNKNOWN; | |
}; | |
}, | |
'node_modules/spotify-live-models/util/bridge.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var request = function (name, args, callback) { | |
SP.request(name, args || [], null, function (data) { | |
if (callback) | |
callback(null, data); | |
}, function (data) { | |
var _args = JSON.stringify(args); | |
var debug = ' (bridge message: \'' + name + '\', args: ' + _args + ')'; | |
var msg = data.message + debug; | |
var error = new Error(msg); | |
error.name = data.error; | |
if (callback) | |
callback(error); | |
}); | |
}; | |
exports.request = request; | |
}, | |
'node_modules/api/arb.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/arb.loc/strings/playlist.lang', { | |
'Starred': 'Starred', | |
'Toplist': 'Top tracks' | |
}); | |
}, | |
'node_modules/api/el.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/el.loc/strings/playlist.lang', { | |
'Starred': '\u0391\u03B3\u03B1\u03C0\u03B7\u03BC\u03AD\u03BD\u03B1', | |
'Toplist': '\u039A\u03BF\u03C1\u03C5\u03C6\u03B1\u03AF\u03B1 \u03C4\u03C1\u03B1\u03B3\u03BF\u03CD\u03B4\u03B9\u03B1' | |
}); | |
}, | |
'node_modules/api/es.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/es.loc/strings/playlist.lang', { | |
'Starred': 'Favoritos', | |
'Toplist': 'Canciones m\xE1s escuchadas' | |
}); | |
}, | |
'node_modules/api/de.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/de.loc/strings/playlist.lang', { | |
'Starred': 'Starred', | |
'Toplist': 'Top-Titel' | |
}); | |
}, | |
'node_modules/api/es-la.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/es-la.loc/strings/playlist.lang', { | |
'Starred': 'Seleccionadas', | |
'Toplist': 'Canciones favoritas' | |
}); | |
}, | |
'node_modules/api/en.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/en.loc/strings/playlist.lang', { | |
'Starred': 'Starred', | |
'Toplist': 'Top tracks' | |
}); | |
}, | |
'node_modules/api/es-419.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/es-419.loc/strings/playlist.lang', { | |
'Starred': 'Seleccionadas', | |
'Toplist': 'Canciones favoritas' | |
}); | |
}, | |
'node_modules/api/fi.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/fi.loc/strings/playlist.lang', { | |
'Starred': 'T\xE4hdell\xE4 merkityt', | |
'Toplist': 'Soitetuimmat kappaleet' | |
}); | |
}, | |
'node_modules/api/fr.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/fr.loc/strings/playlist.lang', { | |
'Starred': 'S\xE9lection', | |
'Toplist': 'Top titres' | |
}); | |
}, | |
'node_modules/api/hu.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/hu.loc/strings/playlist.lang', { | |
'Starred': 'Megcsillagozott', | |
'Toplist': 'N\xE9pszer\u0171 dalok' | |
}); | |
}, | |
'node_modules/api/id.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/id.loc/strings/playlist.lang', { | |
'Starred': 'Diberi bintang', | |
'Toplist': 'Lagu teratas' | |
}); | |
}, | |
'node_modules/api/it.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/it.loc/strings/playlist.lang', { | |
'Starred': 'Preferiti', | |
'Toplist': 'Brani top' | |
}); | |
}, | |
'node_modules/api/ja.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/ja.loc/strings/playlist.lang', { | |
'Starred': '\u30B9\u30BF\u30FC', | |
'Toplist': '\u30C8\u30C3\u30D7\u66F2' | |
}); | |
}, | |
'node_modules/api/nl.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/nl.loc/strings/playlist.lang', { | |
'Starred': 'Favorieten', | |
'Toplist': 'Topnummers' | |
}); | |
}, | |
'node_modules/api/pl.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/pl.loc/strings/playlist.lang', { | |
'Starred': 'Oznaczone gwiazdk\u0105', | |
'Toplist': 'Najlepsze utwory' | |
}); | |
}, | |
'node_modules/api/pt-br.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/pt-br.loc/strings/playlist.lang', { | |
'Starred': 'Favoritos', | |
'Toplist': 'As mais tocadas' | |
}); | |
}, | |
'node_modules/api/ro.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/ro.loc/strings/playlist.lang', { | |
'Starred': 'Marcat cu stea', | |
'Toplist': 'Melodii de top' | |
}); | |
}, | |
'node_modules/api/ru.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/ru.loc/strings/playlist.lang', { | |
'Starred': '\u0418\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0435', | |
'Toplist': '\u041B\u0443\u0447\u0448\u0438\u0435 \u0442\u0440\u0435\u043A\u0438' | |
}); | |
}, | |
'node_modules/api/sv.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/sv.loc/strings/playlist.lang', { | |
'Starred': 'Favoritmarkerad', | |
'Toplist': 'Popul\xE4ra sp\xE5r' | |
}); | |
}, | |
'node_modules/api/th.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/th.loc/strings/playlist.lang', { | |
'Starred': '\u0E43\u0E2B\u0E49\u0E04\u0E30\u0E41\u0E19\u0E19\u0E41\u0E25\u0E49\u0E27', | |
'Toplist': '\u0E41\u0E17\u0E23\u0E47\u0E01\u0E2D\u0E31\u0E19\u0E14\u0E31\u0E1A\u0E15\u0E49\u0E19\u0E46' | |
}); | |
}, | |
'node_modules/api/tr.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/tr.loc/strings/playlist.lang', { | |
'Starred': 'Y\u0131ld\u0131zl\u0131lar', | |
'Toplist': 'En \xE7ok dinlenen par\xE7alar' | |
}); | |
}, | |
'node_modules/api/zh-hant.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/zh-hant.loc/strings/playlist.lang', { | |
'Starred': '\u5DF2\u661F\u8A55', | |
'Toplist': '\u7576\u7D05\u6B4C\u66F2' | |
}); | |
}, | |
'node_modules/api/zsm.loc/strings/playlist.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/api/zsm.loc/strings/playlist.lang', { | |
'Starred': 'Dibintangkan', | |
'Toplist': 'Lagu paling popular' | |
}); | |
}, | |
'node_modules/api/scripts/models.js': function (require, module, exports, global, __filename, __dirname) { | |
var _resolveResult = function (result) { | |
this.object.resolveMany(0, result); | |
this.setDone(); | |
}; | |
var _setDone = function () { | |
this.setDone(); | |
}; | |
var promisedRequest = function (object, request, args, opt_resolveResult) { | |
var promise = new Promise(object); | |
SP.request(request, args, promise, opt_resolveResult ? _resolveResult : _setDone, promise.setFail); | |
return promise; | |
}; | |
var _artists = function (metadataWithUri) { | |
return Artist.fromURI(metadataWithUri.uri, metadataWithUri); | |
}; | |
var _albums = function (metadataWithUri) { | |
return Album.fromURI(metadataWithUri.uri, metadataWithUri); | |
}; | |
var _discs = function (metadataWithUri) { | |
return Disc.fromURI(metadataWithUri.uri, metadataWithUri); | |
}; | |
function Cache(itemClass) { | |
this._items = {}; | |
this._class = itemClass; | |
} | |
Cache.lookup = function (uri, opt_metadata) { | |
return this._cache.lookup(uri, opt_metadata); | |
}; | |
Cache.lookupMany = function (uris) { | |
var result = []; | |
for (var i = 0, len = uris.length; i < len; i++) { | |
result.push(this._cache.lookup(uris[i])); | |
} | |
return result; | |
}; | |
Cache.prototype.cache = function (uri, item) { | |
this._items[uri] = item; | |
}; | |
Cache.prototype.lookup = function (uri, opt_metadata) { | |
if (!uri) | |
return null; | |
var item = this._items[uri]; | |
if (!(item instanceof this._class)) { | |
item = new this._class(uri); | |
item.resolveMany(0, opt_metadata); | |
this._items[uri] = item; | |
} | |
return item; | |
}; | |
Cache.prototype.remove = function (uri) { | |
delete this._items[uri]; | |
}; | |
Cache.prototype.update = function (uris, data) { | |
for (var i = 0, len = uris.length; i < len; i++) { | |
var item = this._items[uris[i]]; | |
if (item) | |
item.resolveMany(0, data); | |
} | |
}; | |
function Observable() { | |
} | |
Observable.prototype._observed = function () { | |
}; | |
Observable.prototype.addEventListener = function (eventType, observer) { | |
if (!observer) | |
return; | |
if (!this._ob) { | |
this._ob = {}; | |
this._obcount = 0; | |
} | |
var callbacks = this._ob[eventType]; | |
if (callbacks) | |
callbacks.push(observer); | |
else | |
this._ob[eventType] = [observer]; | |
this._obcount++; | |
if (this._obcount == 1) | |
this._observed(); | |
}; | |
Observable.prototype.removeEventListener = function (eventType, observer) { | |
var observers = this._ob || {}; | |
var callbacks = observers[eventType] || []; | |
var index = callbacks.indexOf(observer); | |
if (index != -1) { | |
this._obcount--; | |
callbacks.splice(index, 1); | |
if (!callbacks.length) | |
delete observers[eventType]; | |
if (!this._obcount) | |
delete this._ob; | |
} | |
}; | |
Observable.prototype.dispatchEvent = function (evt) { | |
if (typeof evt == 'string') { | |
evt = { type: evt }; | |
} | |
if (!evt || !evt.type) { | |
throw new Error('Dispatched event must have a type.'); | |
} | |
if (!evt.target) { | |
evt.target = this; | |
} | |
var observers = this._ob || {}; | |
var callbacks = (observers[evt.type] || []).slice(0); | |
if (!callbacks.length) | |
return true; | |
var ret = true; | |
evt.preventDefault = function () { | |
ret = false; | |
}; | |
for (var i = 0; i < callbacks.length; i++) { | |
try { | |
if (callbacks[i].call(this, evt) === false) | |
ret = false; | |
} catch (error) { | |
console.error(error); | |
if (SP._throwError) | |
throw error; | |
} | |
} | |
return ret; | |
}; | |
function Promise(opt_object) { | |
this.object = opt_object; | |
this._done = []; | |
this._fail = []; | |
} | |
Promise.prototype.always = function (callbackOrThis, opt_callback) { | |
var cbFunc, cbThis; | |
if (opt_callback) { | |
cbFunc = opt_callback; | |
cbThis = callbackOrThis; | |
} else { | |
cbFunc = callbackOrThis; | |
cbThis = this; | |
} | |
if (typeof cbFunc != 'function') | |
throw new Error('A callback function is required'); | |
if (this._done) { | |
this._done.push(SP.callback(SP.bind(cbFunc, cbThis))); | |
this._fail.push(SP.callback(SP.bind(cbFunc, cbThis))); | |
} else { | |
cbFunc.apply(cbThis, this._args); | |
} | |
return this; | |
}; | |
Promise.prototype.done = function (callbackOrThis, opt_callback) { | |
var cbFunc, cbThis; | |
if (opt_callback) { | |
cbFunc = opt_callback; | |
cbThis = callbackOrThis; | |
} else { | |
cbFunc = callbackOrThis; | |
cbThis = this; | |
} | |
if (typeof cbFunc != 'function') | |
throw new Error('A callback function is required'); | |
if (this._isDone) | |
cbFunc.apply(cbThis, this._args); | |
else if (this._done) | |
this._done.push(SP.callback(SP.bind(cbFunc, cbThis))); | |
return this; | |
}; | |
Promise.prototype.fail = function (callbackOrThis, opt_callback) { | |
var cbFunc, cbThis; | |
if (opt_callback) { | |
cbFunc = opt_callback; | |
cbThis = callbackOrThis; | |
} else { | |
cbFunc = callbackOrThis; | |
cbThis = this; | |
} | |
if (typeof cbFunc != 'function') | |
throw new Error('A callback function is required'); | |
if (this._isFail) | |
cbFunc.apply(cbThis, this._args); | |
else if (this._fail) | |
this._fail.push(SP.callback(SP.bind(cbFunc, cbThis))); | |
return this; | |
}; | |
Promise.prototype.each = function (callbackOrThis, opt_callback) { | |
if (this._objs) { | |
var cbFunc, cbThis; | |
if (opt_callback) { | |
cbFunc = opt_callback; | |
cbThis = callbackOrThis; | |
} else { | |
cbFunc = callbackOrThis; | |
cbThis = this; | |
} | |
if (typeof cbFunc != 'function') | |
throw new Error('A callback function is required'); | |
if (this._each) { | |
this._each.push(SP.callback(SP.bind(cbFunc, cbThis))); | |
} | |
for (var i = 0, l = this._objs.length; i < l; i++) | |
cbFunc.call(cbThis, this._objs[i]); | |
} | |
return this; | |
}; | |
Promise.prototype.setDone = function (opt_object) { | |
if (!this._done) | |
return; | |
var done = this._done; | |
var fail = this._fail; | |
delete this._done; | |
delete this._fail; | |
if (arguments.length == 1) { | |
this.object = opt_object; | |
} | |
this._isDone = true; | |
this._args = [this.object]; | |
for (var i = 0, l = done.length; i < l; i++) | |
done[i].apply(undefined, this._args); | |
for (var j = 0, k = fail.length; j < k; j++) | |
fail[j].clear(); | |
delete this._each; | |
delete this._join; | |
delete this._numResolved; | |
delete this._oneFailed; | |
}; | |
Promise.prototype.setFail = function (error) { | |
if (!this._done) | |
return; | |
var fail = this._fail; | |
var done = this._done; | |
delete this._done; | |
delete this._fail; | |
this._isFail = true; | |
this._args = [ | |
this.object, | |
error | |
]; | |
for (var i = 0, l = fail.length; i < l; i++) | |
fail[i].apply(undefined, this._args); | |
for (var j = 0, k = done.length; j < k; j++) | |
done[j].clear(); | |
delete this._each; | |
delete this._join; | |
delete this._numResolved; | |
delete this._oneFailed; | |
}; | |
Promise.join = function (promises) { | |
var promise = new Promise(); | |
promises = SP.varargs(arguments, 0, true); | |
promise._join = promises; | |
promise._each = []; | |
promise._objs = []; | |
promise._numResolved = 0; | |
if (promises.length === 0) | |
promise.setDone([]); | |
for (var i = 0, l = promises.length; i < l; i++) | |
promises[i].done(promise, promise._oneDone).fail(promise, promise._oneFail); | |
return promise; | |
}; | |
Promise.prototype._oneEither = function (object) { | |
this._numResolved++; | |
if (this._numResolved < this._join.length) | |
return; | |
this.object = []; | |
for (var i = 0, l = this._join.length; i < l; i++) | |
this.object.push(this._join[i].object); | |
for (var j = 0, k = this._each.length; j < k; j++) | |
this._each[j].clear(); | |
if (this._oneFailed) | |
this.setFail(); | |
else | |
this.setDone(); | |
}; | |
Promise.prototype._oneDone = function (object) { | |
if (!this._done) | |
return; | |
this._objs.push(object); | |
var nextEach = []; | |
for (var i = 0, l = this._each.length; i < l; i++) { | |
var cb = this._each[i]; | |
nextEach.push(cb.copy()); | |
cb.call(undefined, object); | |
} | |
this._each = nextEach; | |
this._oneEither(object); | |
}; | |
Promise.prototype._oneFail = function (object, error) { | |
if (!this._done) | |
return; | |
this._oneFailed = true; | |
this._oneEither(object); | |
}; | |
function Loadable() { | |
Observable.call(this); | |
} | |
SP.inherit(Loadable, Observable); | |
Loadable.define = function (clazz, names, opt_func) { | |
var proto = clazz.prototype; | |
if (!proto._prop) | |
proto._prop = {}; | |
if (!proto._next) | |
proto._next = 0; | |
var group = { | |
mask: 0, | |
func: opt_func | |
}; | |
for (var i = 0, l = names.length; i < l; i++) { | |
var mask = 1 << proto._next++; | |
group.mask |= mask; | |
proto._prop[names[i]] = { | |
mask: mask, | |
group: group | |
}; | |
} | |
}; | |
Loadable.prototype._make = function (name, value) { | |
name = '_make_' + name; | |
var func = this[name]; | |
return func ? func(value) : value; | |
}; | |
Loadable.prototype.resolve = function (name, value, opt_silent) { | |
var prop = this._prop[name]; | |
if (!prop) | |
return; | |
this._done |= this._prop[name].mask; | |
this._wait &= ~this._done; | |
var newValue = this._make(name, value); | |
if (this.hasOwnProperty(name) && !opt_silent) { | |
var oldValue = this[name]; | |
if (oldValue !== newValue) { | |
this[name] = newValue; | |
this.dispatchEvent({ | |
type: 'change:' + name, | |
property: name, | |
oldValue: oldValue | |
}); | |
} | |
} else { | |
this[name] = newValue; | |
} | |
if (!this._wait) | |
delete this._wait; | |
}; | |
Loadable.prototype.resolveMany = function (propsMask, data, opt_silent) { | |
for (var name in data) | |
this.resolve(name, data[name], opt_silent); | |
this._done |= propsMask; | |
this._wait &= ~propsMask; | |
this.resolveDone(); | |
}; | |
Loadable.prototype.resolveDone = function () { | |
if (!this._reqs) | |
return; | |
var done = []; | |
for (var i = 0; i < this._reqs.length; i++) { | |
if (!(this._reqs[i]._need & ~this._done)) | |
done.push(this._reqs.splice(i--, 1)[0]); | |
} | |
if (!this._reqs.length) | |
delete this._reqs; | |
if (!this._wait) | |
delete this._wait; | |
for (var j = 0, l = done.length; j < l; j++) { | |
done[j].setDone(); | |
} | |
}; | |
Loadable.prototype.resolveFail = function (propsMask, error) { | |
this._wait &= ~propsMask; | |
if (!this._reqs) | |
return; | |
var fail = []; | |
for (var i = 0; i < this._reqs.length; i++) { | |
if (this._reqs[i]._need & propsMask) | |
fail.push(this._reqs.splice(i--, 1)[0]); | |
} | |
if (!this._reqs.length) | |
delete this._reqs; | |
if (!this._wait) | |
delete this._wait; | |
for (var j = 0, l = fail.length; j < l; j++) { | |
fail[j].setFail(error); | |
} | |
}; | |
Loadable.prototype.load = function (properties) { | |
var args = SP.varargs(arguments); | |
var req = new Promise(this); | |
req._need = this._neededForLoad(args); | |
if (req._need) { | |
if (this._reqs) | |
this._reqs.push(req); | |
else | |
this._reqs = [req]; | |
this._requestProperties(req._need); | |
} else { | |
req.setDone(); | |
} | |
return req; | |
}; | |
Loadable.prototype._neededForLoad = function (properties) { | |
var neededMask = 0; | |
for (var i = 0, l = properties.length; i < l; i++) { | |
var name = properties[i]; | |
var prop = this._prop[name]; | |
if (!prop) | |
throw new Error(name + ' is not a property.'); | |
neededMask |= prop.mask; | |
} | |
return neededMask & ~this._done; | |
}; | |
Loadable.prototype._requestProperties = function (propsMask) { | |
var groups = []; | |
for (var name in this._prop) { | |
var prop = this._prop[name]; | |
var mask = prop.group.mask; | |
if (!(mask & propsMask)) | |
continue; | |
if (mask & this._wait) | |
continue; | |
groups.push(prop.group); | |
this._wait |= mask; | |
propsMask &= ~mask; | |
if (!propsMask) | |
break; | |
} | |
for (var i = 0, l = groups.length; i < l; i++) { | |
var func = this[groups[i].func]; | |
if (func) | |
func.call(this, groups[i].mask); | |
} | |
}; | |
function BridgeLoadable() { | |
Loadable.call(this); | |
} | |
SP.inherit(BridgeLoadable, Loadable); | |
BridgeLoadable.prototype.bridgeListen = function (requestName, requestArgs) { | |
if (!this._listening) { | |
this._requestName = requestName; | |
this._requestArgs = requestArgs; | |
this._listening = true; | |
this._eventWait(); | |
} | |
}; | |
BridgeLoadable.prototype.bridgeUnlisten = function () { | |
delete this._requestName; | |
delete this._requestArgs; | |
delete this._listening; | |
}; | |
BridgeLoadable.prototype._eventWait = function () { | |
if (this._listening) | |
SP.request(this._requestName, this._requestArgs, this, this._eventDone, this._eventFail); | |
}; | |
BridgeLoadable.prototype._eventDone = function (event) { | |
this._eventWait(); | |
this.eventDone(event); | |
}; | |
BridgeLoadable.prototype.eventDone = function (event) { | |
if (event.receiver && this.hasOwnProperty(event.receiver)) { | |
var receiver = this[event.receiver]; | |
receiver.resolveMany(0, event.data); | |
receiver.dispatchEvent(event); | |
} else { | |
this.resolveMany(0, event.data); | |
this.dispatchEvent(event); | |
} | |
}; | |
BridgeLoadable.prototype._eventFail = function (error) { | |
if (error.error == 'timeout') | |
this._eventWait(); | |
this.eventFail(error); | |
}; | |
BridgeLoadable.prototype.eventFail = function (error) { | |
}; | |
function ProxyListener() { | |
BridgeLoadable.call(this); | |
this._filters = []; | |
this._receivers = []; | |
} | |
SP.inherit(ProxyListener, BridgeLoadable); | |
ProxyListener.prototype.filter = function (filter) { | |
this._filters.push(filter); | |
}; | |
ProxyListener.prototype.proxyTo = function (receiver) { | |
this._receivers.push(receiver); | |
}; | |
ProxyListener.prototype.eventDone = function (evt) { | |
var i, len, proxy = true; | |
for (i = 0, len = this._filters.length; i < len; i++) { | |
if (this._filters[i](evt) === false) | |
proxy = false; | |
} | |
if (!proxy) | |
return; | |
for (i = 0, len = this._receivers.length; i < len; i++) { | |
this._receivers[i].eventDone(evt); | |
} | |
}; | |
function MdL(uri) { | |
BridgeLoadable.call(this); | |
} | |
SP.inherit(MdL, BridgeLoadable); | |
MdL.init = function (clazz, prefix) { | |
clazz._type = prefix; | |
}; | |
MdL.prototype.imageForSize = function (size) { | |
var images = this.images; | |
size *= window.devicePixelRatio || 1; | |
for (var i = 0, l = images ? images.length : 0; i < l; i++) { | |
if (images[i][0] >= size || i == l - 1) | |
return images[i][1].replace('{size}', size); | |
} | |
return this.image; | |
}; | |
MdL.prototype._metadata = function (propsMask) { | |
var load = function (data) { | |
this.resolveMany(propsMask, data); | |
}; | |
var fail = function (oops) { | |
this.resolveFail(propsMask, oops); | |
}; | |
SP.request(this.constructor._type + '_metadata', [this.uri], this, load, fail); | |
}; | |
MdL.prototype._profile = function (propsMask) { | |
var load = function (data) { | |
this.resolveMany(propsMask, data); | |
}; | |
var fail = function (oops) { | |
this.resolveFail(propsMask, oops); | |
}; | |
SP.request(this.constructor._type + '_profile', [this.uri], this, load, fail); | |
}; | |
MdL.prototype.toString = function () { | |
return this.uri; | |
}; | |
function Album(uri) { | |
MdL.call(this); | |
this.resolve('uri', uri); | |
} | |
SP.inherit(Album, MdL); | |
Loadable.define(Album, ['uri']); | |
Loadable.define(Album, [ | |
'availability', | |
'artists', | |
'date', | |
'discs', | |
'image', | |
'images', | |
'label', | |
'name', | |
'playable', | |
'popularity', | |
'type' | |
], '_metadata'); | |
Loadable.define(Album, ['copyrights'], '_profile'); | |
Loadable.define(Album, ['tracks'], '_collections'); | |
MdL.init(Album, 'album'); | |
Album.prototype._make_artists = function (value) { | |
return value && value.map(_artists); | |
}; | |
Album.prototype._make_discs = function (value) { | |
return value && value.map(_discs); | |
}; | |
Album.prototype._collections = function () { | |
this.resolve('tracks', new BridgeCollection(Track, this.uri, 'album_tracks')); | |
this.resolveDone(); | |
}; | |
Album.fromURI = Cache.lookup; | |
Album.fromURIs = Cache.lookupMany; | |
Album._cache = new Cache(Album); | |
function Disc(uri) { | |
MdL.call(this); | |
this.resolve('uri', uri); | |
this.resolve('tracks', new BridgeCollection(Track, uri, 'album_disc_tracks')); | |
} | |
SP.inherit(Disc, MdL); | |
Loadable.define(Disc, [ | |
'uri', | |
'tracks' | |
]); | |
Loadable.define(Disc, [ | |
'album', | |
'number' | |
], '_metadata'); | |
MdL.init(Disc, 'disc'); | |
Disc.prototype._make_album = function (value) { | |
return value && Album.fromURI(value); | |
}; | |
Disc.fromURI = Cache.lookup; | |
Disc.fromURIs = Cache.lookupMany; | |
Disc._cache = new Cache(Disc); | |
function AlbumGroup(uri, metadata) { | |
Loadable.call(this); | |
this.resolve('albums', metadata && metadata.albums ? metadata.albums.map(_albums) : []); | |
} | |
SP.inherit(AlbumGroup, Loadable); | |
Loadable.define(AlbumGroup, ['albums']); | |
AlbumGroup.fromURI = function (uri, metadata) { | |
return new this(uri, metadata); | |
}; | |
function Client() { | |
BridgeLoadable.call(this); | |
} | |
SP.inherit(Client, BridgeLoadable); | |
Client.prototype._observed = function () { | |
this.bridgeListen('client_event_wait', []); | |
}; | |
Loadable.define(Client, ['features'], '_features'); | |
Loadable.define(Client, ['hide_hpto'], '_hide_hpto'); | |
Client.prototype._features = function (propsMask) { | |
var load = function (data) { | |
this.resolveMany(propsMask, data); | |
}; | |
var fail = function (oops) { | |
this.resolveFail(propsMask, oops); | |
}; | |
SP.request('client_features', [], this, load, fail); | |
}; | |
Client.prototype._hide_hpto = function (propsMask) { | |
var load = function (data) { | |
this.resolveMany(propsMask, data); | |
}; | |
var fail = function (oops) { | |
this.resolveFail(propsMask, oops); | |
}; | |
SP.request('client_get_hide_hpto', [], this, load, fail); | |
}; | |
Client.prototype.showShareUI = function (item, opt_message, opt_point) { | |
var uri = item.uri || item; | |
var message = opt_message || ''; | |
var args = [ | |
uri, | |
message | |
]; | |
if (opt_point && 'x' in opt_point && 'y' in opt_point) { | |
args.push(opt_point.x); | |
args.push(opt_point.y); | |
} | |
return promisedRequest(this, 'client_show_share_ui', args); | |
}; | |
Client.prototype.showContextUI = function (items, opt_point, opt_origin, opt_index) { | |
var uris = Array.isArray(items) ? SP.uris(items) : [items.uri]; | |
var args = [uris]; | |
if (opt_point && 'x' in opt_point && 'y' in opt_point) { | |
args.push(opt_point.x); | |
args.push(opt_point.y); | |
} | |
if (opt_origin && opt_origin.uri) { | |
args.push(opt_origin.uri); | |
} | |
if (typeof opt_index !== 'undefined' && opt_index % 1 === 0) { | |
args.push(opt_index); | |
} | |
return promisedRequest(this, 'client_show_context_ui', args); | |
}; | |
Client.prototype.broadcast = function (message) { | |
return promisedRequest(this, 'client_broadcast', [message]); | |
}; | |
function Application() { | |
BridgeLoadable.call(this); | |
} | |
SP.inherit(Application, BridgeLoadable); | |
Loadable.define(Application, [ | |
'arguments', | |
'dropped', | |
'identifier', | |
'name', | |
'uri' | |
], '_query'); | |
Application.prototype._observed = function () { | |
this.bridgeListen('application_event_wait', []); | |
}; | |
Application.prototype._make_dropped = function (value) { | |
return value && value.map(function (i) { | |
return fromURI(i); | |
}); | |
}; | |
Application.prototype._query = function (propsMask) { | |
var load = function (data) { | |
this.resolveMany(propsMask, data); | |
}; | |
var fail = function (oops) { | |
this.resolveFail(propsMask, oops); | |
}; | |
SP.request('application_query', [], this, load, fail); | |
}; | |
Application.prototype.activate = function () { | |
return promisedRequest(this, 'application_activate', [this.uri]); | |
}; | |
Application.prototype.deactivate = function () { | |
return promisedRequest(this, 'application_deactivate', [this.uri]); | |
}; | |
Application.prototype.exit = function (opt_statusCode) { | |
return promisedRequest(this, 'application_notify_exit', [opt_statusCode || 0]); | |
}; | |
Application.prototype.hideLoadingScreen = function () { | |
SP.request('application_notify_loaded', []); | |
}; | |
Application.prototype.readFile = function (path) { | |
var promise = new Promise(); | |
var request = new XMLHttpRequest(); | |
request.open('GET', path, true); | |
request.onreadystatechange = function (e) { | |
if (request.readyState !== 4) | |
return; | |
if (request.status !== 200 && request.status !== 0) { | |
promise.setFail(); | |
} else { | |
promise.setDone(request.responseText); | |
} | |
}; | |
request.send(null); | |
return promise; | |
}; | |
Application.prototype.openURI = function (uri, opt_context) { | |
return promisedRequest(this, 'application_open_uri', [ | |
uri, | |
opt_context || null | |
]); | |
}; | |
Application.prototype.openApp = function (app, var_args) { | |
var arg = SP.varargs(arguments, 1); | |
var uriSegments = [ | |
'spotify', | |
'app', | |
app | |
]; | |
for (var i = 0, l = arg.length; i < l; i++) { | |
uriSegments.push(encodeURIComponent(arg[i])); | |
} | |
return this.openURI(uriSegments.join(':')); | |
}; | |
Application.prototype.setTitle = function (title, opt_subtitle) { | |
return promisedRequest(this, 'application_set_title', [ | |
title, | |
opt_subtitle || '' | |
]); | |
}; | |
Application.prototype.setPreferredSize = function (width, height) { | |
var promise = new Promise(); | |
var args = [ | |
width, | |
height | |
]; | |
SP.request('application_set_preferred_size', args, promise, promise.setDone, promise.setFail); | |
return promise; | |
}; | |
Application.prototype.resolvePath = function (path) { | |
return SP.resolvePath(path); | |
}; | |
Application.prototype.clientEvent = function (context, event, eventVersion, testVersion, data) { | |
return promisedRequest(this, 'application_client_event', [].slice.call(arguments)); | |
}; | |
Application.prototype.bannerShownEvent = function (eventInfo) { | |
return promisedRequest(this, 'application_banner_shown_event', eventInfo); | |
}; | |
function Artist(uri) { | |
MdL.call(this); | |
this.resolve('uri', uri); | |
} | |
SP.inherit(Artist, MdL); | |
Loadable.define(Artist, ['uri']); | |
Loadable.define(Artist, [ | |
'image', | |
'images', | |
'name', | |
'popularity' | |
], '_metadata'); | |
Loadable.define(Artist, [ | |
'biography', | |
'genres', | |
'portraits', | |
'years' | |
], '_profile'); | |
Loadable.define(Artist, [ | |
'albums', | |
'appearances', | |
'compilations', | |
'related', | |
'singles' | |
], '_collections'); | |
Loadable.define(Artist, ['user'], '_associatedUser'); | |
MdL.init(Artist, 'artist'); | |
Artist.prototype._collections = function () { | |
this.resolve('albums', new BridgeCollection(AlbumGroup, this.uri, 'artist_albums')); | |
this.resolve('appearances', new BridgeCollection(AlbumGroup, this.uri, 'artist_appearances')); | |
this.resolve('compilations', new BridgeCollection(AlbumGroup, this.uri, 'artist_compilations')); | |
this.resolve('related', new BridgeCollection(Artist, this.uri, 'artist_related_artists')); | |
this.resolve('singles', new BridgeCollection(AlbumGroup, this.uri, 'artist_singles')); | |
this.resolveDone(); | |
}; | |
Artist.prototype._associatedUser = function (propsMask) { | |
var load = function (data) { | |
this.resolveMany(propsMask, data); | |
}; | |
var fail = function (oops) { | |
this.resolveFail(propsMask, oops); | |
}; | |
SP.request('artist_associated_user', [this.uri], this, load, fail); | |
}; | |
Artist.prototype._make_user = function (value) { | |
return value && User.fromURI(value); | |
}; | |
Artist.fromURI = Cache.lookup; | |
Artist.fromURIs = Cache.lookupMany; | |
Artist._cache = new Cache(Artist); | |
ListDescriptor.Types = { | |
LIST: 'list', | |
LISTS: 'lists', | |
SORT: 'sort', | |
FILTER: 'filter', | |
RANGE: 'range', | |
SHUFFLE: 'shuffle' | |
}; | |
function ListDescriptor(type, opt_params) { | |
this.type = type; | |
for (var n in opt_params) { | |
this[n] = opt_params[n]; | |
} | |
} | |
ListDescriptor.compare = function (a, b) { | |
if (a.type !== b.type) { | |
return false; | |
} | |
switch (a.type) { | |
case ListDescriptor.Types.LIST: | |
return a.uri === b.uri; | |
case ListDescriptor.Types.LISTS: | |
if (a.lists.length !== b.lists.length) { | |
return false; | |
} | |
for (var i = 0; i < a.lists.length; i++) { | |
if (!ListDescriptor.compare(a.lists[i], b.lists[i])) { | |
return false; | |
} | |
} | |
return true; | |
case ListDescriptor.Types.FILTER: | |
case ListDescriptor.Types.RANGE: | |
case ListDescriptor.Types.SHUFFLE: | |
case ListDescriptor.Types.SORT: | |
if (a.version !== b.version) { | |
return false; | |
} | |
if (a.args.toString() !== b.args.toString()) { | |
return false; | |
} | |
return ListDescriptor.compare(a.list, b.list); | |
default: | |
return false; | |
} | |
}; | |
ListDescriptor.create = function (uri) { | |
return new ListDescriptor(ListDescriptor.Types.LIST, { uri: uri }); | |
}; | |
ListDescriptor.createConcatenated = function (lists) { | |
return new ListDescriptor(ListDescriptor.Types.LISTS, { lists: lists }); | |
}; | |
ListDescriptor.prototype.filter = function (operation, field, value) { | |
return new ListDescriptor(ListDescriptor.Types.FILTER, { | |
list: this, | |
args: [ | |
operation, | |
field, | |
value | |
], | |
version: 1 | |
}); | |
}; | |
ListDescriptor.prototype.range = function (offset, length) { | |
return new ListDescriptor(ListDescriptor.Types.RANGE, { | |
list: this, | |
args: [ | |
offset, | |
length | |
], | |
version: 1 | |
}); | |
}; | |
ListDescriptor.prototype.shuffle = function (opt_seed) { | |
return new ListDescriptor(ListDescriptor.Types.SHUFFLE, { | |
list: this, | |
args: [opt_seed || Math.floor(Math.random() * 200000000)], | |
version: 1 | |
}); | |
}; | |
ListDescriptor.prototype.sort = function (field, opt_direction, var_args) { | |
if (arguments.length > 2 && arguments.length % 2 != 0) { | |
throw new Error('Invalid number of parameters'); | |
} | |
var args = arguments.length == 1 ? [ | |
field, | |
'asc' | |
] : Array.prototype.slice.call(arguments); | |
return new ListDescriptor(ListDescriptor.Types.SORT, { | |
list: this, | |
args: args, | |
version: 1 | |
}); | |
}; | |
ListDescriptor.prototype.getBase = function () { | |
switch (this.type) { | |
case ListDescriptor.Types.LIST: | |
case ListDescriptor.Types.LISTS: | |
return this; | |
case ListDescriptor.Types.FILTER: | |
case ListDescriptor.Types.RANGE: | |
case ListDescriptor.Types.SHUFFLE: | |
case ListDescriptor.Types.SORT: | |
return this.list ? this.list.getBase() : null; | |
} | |
return null; | |
}; | |
function Collection(itemClass, uri, snapshot, opt_descriptor, opt_itemFactory) { | |
BridgeLoadable.call(this); | |
this.resolve('descriptor', opt_descriptor instanceof ListDescriptor ? opt_descriptor : ListDescriptor.create(opt_descriptor || uri)); | |
this.resolve('type', itemClass); | |
this.resolve('uri', uri); | |
this._snapshot = snapshot; | |
this._factory = opt_itemFactory || SP.bind(itemClass.fromURI, itemClass); | |
} | |
SP.inherit(Collection, BridgeLoadable); | |
Loadable.define(Collection, [ | |
'descriptor', | |
'type', | |
'uri' | |
]); | |
Collection.prototype.clone = function (opt_newDescriptor) { | |
return new Collection(this.type, this.uri, this._snapshot, opt_newDescriptor || this.descriptor, this._factory); | |
}; | |
Collection.prototype.snapshot = function (opt_start, opt_length, opt_raw) { | |
var snapshot = new Snapshot(this, opt_start, opt_length, opt_raw); | |
return snapshot.load('length', 'range'); | |
}; | |
Collection.prototype.add = function (items) { | |
throw new Error('This method has not been implemented.'); | |
}; | |
Collection.prototype.insert = function (ref, items) { | |
throw new Error('This method has not been implemented.'); | |
}; | |
Collection.prototype.remove = function (ref) { | |
throw new Error('This method has not been implemented.'); | |
}; | |
Collection.prototype.trim = function (ref) { | |
throw new Error('This method has not been implemented.'); | |
}; | |
Collection.prototype.clear = function () { | |
throw new Error('This method has not been implemented.'); | |
}; | |
Collection.prototype.sort = function (field, opt_direction, var_args) { | |
return this.clone(this.descriptor.sort.apply(this.descriptor, arguments)); | |
}; | |
Collection.prototype.filter = function (operation, field, value) { | |
return this.clone(this.descriptor.filter(operation, field, value)); | |
}; | |
Collection.prototype.range = function (offset, length) { | |
return this.clone(this.descriptor.range(offset, length)); | |
}; | |
Collection.prototype.shuffle = function (opt_seed) { | |
return this.clone(this.descriptor.shuffle(opt_seed)); | |
}; | |
Collection.prototype.contains = function (items) { | |
throw new Error('This method has not been implemented.'); | |
}; | |
function BridgeCollection(itemClass, uri, requestPrefix, opt_descriptor, opt_itemFactory) { | |
Collection.call(this, itemClass, uri, this._requestSnapshot, opt_descriptor, opt_itemFactory); | |
this._prefix = requestPrefix; | |
} | |
SP.inherit(BridgeCollection, Collection); | |
BridgeCollection.prototype._requestSnapshot = function (descriptor, offset, length, raw) { | |
var promise = new Promise(); | |
if (this._prefix.indexOf('toplist_region_') == 0 && descriptor.uri.match(/:country:USER$/) != null) { | |
var onCountryLoaded = function (session) { | |
descriptor.uri = descriptor.uri.replace(/:country:USER$/, ':country:' + session.country); | |
SP.request(this._prefix + '_snapshot', [ | |
descriptor, | |
offset, | |
length, | |
raw | |
], promise, promise.setDone, promise.setFail); | |
}; | |
new Session().load('country').done(SP.bind(onCountryLoaded, this)).fail(promise.setFail); | |
return promise; | |
} | |
SP.request(this._prefix + '_snapshot', [ | |
descriptor, | |
offset, | |
length, | |
raw | |
], promise, promise.setDone, promise.setFail); | |
return promise; | |
}; | |
BridgeCollection.prototype.add = function (items) { | |
var args = SP.uris(arguments); | |
args.unshift(this.descriptor); | |
return promisedRequest(this, this._prefix + '_append', args); | |
}; | |
BridgeCollection.prototype.clear = function () { | |
return promisedRequest(this, this._prefix + '_clear', [this.descriptor]); | |
}; | |
BridgeCollection.prototype.clone = function (opt_newDescriptor) { | |
return new BridgeCollection(this.type, this.uri, this._prefix, opt_newDescriptor || this.descriptor, this._factory); | |
}; | |
BridgeCollection.prototype.insert = function (ref, items) { | |
var args = [ | |
this.descriptor, | |
ref.index, | |
ref.uri | |
]; | |
var uris = SP.uris(arguments, 1); | |
return promisedRequest(this, this._prefix + '_insert', args.concat(uris)); | |
}; | |
BridgeCollection.prototype.remove = function (ref) { | |
return promisedRequest(this, this._prefix + '_remove', [ | |
this.descriptor, | |
ref.index, | |
ref.uri | |
]); | |
}; | |
BridgeCollection.prototype.trim = function (ref) { | |
return promisedRequest(this, this._prefix + '_trim', [ | |
this.descriptor, | |
ref.index, | |
ref.uri | |
]); | |
}; | |
BridgeCollection.prototype.contains = function (items) { | |
var args = SP.uris(arguments); | |
args.unshift(this.descriptor); | |
var promise = new Promise(); | |
var done = function (val) { | |
if (args.length == 2 && !(items instanceof Array)) | |
promise.object = val.in_collection[0]; | |
else | |
promise.object = val.in_collection; | |
promise.setDone(); | |
}; | |
SP.request(this._prefix + '_contains', args, promise, done, promise.setFail); | |
return promise; | |
}; | |
function Context(uri) { | |
Loadable.call(this); | |
this.resolve('uri', uri); | |
} | |
SP.inherit(Context, Loadable); | |
Loadable.define(Context, ['uri']); | |
Context.prototype.toString = function () { | |
return this.uri; | |
}; | |
Context.fromURI = function (uri) { | |
return new Context(uri); | |
}; | |
function Group() { | |
Loadable.call(this); | |
this.resolve('descriptor', ListDescriptor.createConcatenated([])); | |
} | |
SP.inherit(Group, Loadable); | |
Loadable.define(Group, ['descriptor']); | |
Group.prototype.add = function (context) { | |
var descriptor = context.descriptor || ListDescriptor.create(context.uri); | |
this.descriptor.lists.push(descriptor); | |
}; | |
Group.create = function () { | |
var group = new Group(); | |
var promise = new Promise(); | |
promise.setDone(group); | |
return promise; | |
}; | |
function Player(id) { | |
BridgeLoadable.call(this); | |
this.resolve('id', id); | |
} | |
SP.inherit(Player, BridgeLoadable); | |
Loadable.define(Player, [ | |
'context', | |
'contexts', | |
'duration', | |
'id', | |
'index', | |
'playing', | |
'repeat', | |
'shuffle', | |
'track', | |
'volume' | |
], '_query'); | |
Loadable.define(Player, ['position'], '_position'); | |
Player.prototype._observed = function () { | |
this.bridgeListen('player_event_wait', [this.id]); | |
}; | |
Player.prototype.eventDone = function (event) { | |
Player._superClass.eventDone.call(this, event); | |
this._queryPosition(); | |
}; | |
Player.prototype._make_context = function (value) { | |
return value && Context.fromURI(value.uri, value); | |
}; | |
Player.prototype._make_track = function (value) { | |
return value && Track.fromURI(value.uri, value); | |
}; | |
Player.prototype._query = function (propsMask) { | |
var load = function (data) { | |
delete data.position; | |
this.resolveMany(propsMask, data); | |
}; | |
var fail = function (error) { | |
this.resolveFail(propsMask, error); | |
}; | |
SP.request('player_query', [this.id], this, load, fail); | |
this.bridgeListen('player_event_wait', [this.id]); | |
}; | |
Player.prototype._position = function (propsMask) { | |
this._needsPosition = true; | |
this._queryPosition(true); | |
}; | |
Player.prototype._queryPosition = function (opt_immediate) { | |
if (this._needsPosition) { | |
if (opt_immediate || !this._pq && this.playing) { | |
var time = opt_immediate ? 0 : 900; | |
var self = this; | |
this._pq = setTimeout(function () { | |
SP.request('player_query', [self.id], self, self._progress); | |
}, time); | |
} | |
} | |
}; | |
Player.prototype._progress = function (data) { | |
this._pq = null; | |
this.resolve('position', data.position, true); | |
delete data.position; | |
this.resolveMany(0, data); | |
this._queryPosition(); | |
}; | |
Player.prototype.mapTrackIdentifiers = function (map) { | |
return promisedRequest(this, 'player_map_track_identifiers', [ | |
this.id, | |
map | |
]); | |
}; | |
Player.prototype.setVolume = function (volume) { | |
return promisedRequest(this, 'player_set_volume', [ | |
this.id, | |
volume | |
]); | |
}; | |
Player.prototype.setRepeat = function (enabled) { | |
return promisedRequest(this, 'player_set_repeat', [ | |
this.id, | |
enabled | |
]); | |
}; | |
Player.prototype.setShuffle = function (enabled) { | |
return promisedRequest(this, 'player_set_shuffle', [ | |
this.id, | |
enabled | |
]); | |
}; | |
Player.prototype.play = function () { | |
return promisedRequest(this, 'player_play', [this.id]); | |
}; | |
Player.prototype.pause = function () { | |
return promisedRequest(this, 'player_pause', [this.id]); | |
}; | |
Player.prototype.stop = function () { | |
return promisedRequest(this, 'player_stop', [this.id]); | |
}; | |
Player.prototype.playTrack = function (track, ms, duration) { | |
return promisedRequest(this, 'player_play_track', [ | |
this.id, | |
track.uri, | |
ms || 0, | |
duration != undefined ? duration : -1 | |
]); | |
}; | |
Player.prototype.playContext = function (context, index, ms, duration) { | |
if (index == null) | |
index = -1; | |
var descriptor = context.descriptor || ListDescriptor.create(context.uri); | |
return promisedRequest(this, 'player_play_context', [ | |
this.id, | |
descriptor, | |
index, | |
ms || 0, | |
duration != undefined ? duration : -1 | |
]); | |
}; | |
Player.prototype.playContextGroup = function (group, contextIndex, index, ms) { | |
if (contextIndex == undefined) | |
contextIndex = -1; | |
if (index == undefined) | |
index = -1; | |
return promisedRequest(this, 'player_play_context_group', [ | |
this.id, | |
group.descriptor, | |
contextIndex, | |
index, | |
ms || 0 | |
]); | |
}; | |
Player.prototype.skipToPrevTrack = function () { | |
return promisedRequest(this, 'player_skip_to_prev', [this.id]); | |
}; | |
Player.prototype.skipToNextTrack = function () { | |
return promisedRequest(this, 'player_skip_to_next', [this.id]); | |
}; | |
Player.prototype.seek = function (ms) { | |
return promisedRequest(this, 'player_seek', [ | |
this.id, | |
ms | |
]); | |
}; | |
function Playlist(uri) { | |
MdL.call(this); | |
this.resolve('uri', uri); | |
} | |
SP.inherit(Playlist, MdL); | |
Loadable.define(Playlist, ['uri']); | |
Loadable.define(Playlist, [ | |
'allows', | |
'collaborative', | |
'description', | |
'subscribed', | |
'name', | |
'owner', | |
'published' | |
], '_metadata'); | |
Loadable.define(Playlist, [ | |
'image', | |
'images' | |
], '_profile'); | |
Loadable.define(Playlist, [ | |
'subscribers', | |
'tracks' | |
], '_collections'); | |
Loadable.define(Playlist, ['popularity'], '_popularity'); | |
MdL.init(Playlist, 'playlist'); | |
Playlist.prototype._make_owner = function (value) { | |
return value && User.fromURI(value.uri, value); | |
}; | |
Playlist.prototype._collections = function () { | |
this.resolve('subscribers', new BridgeCollection(User, this.uri, 'playlist_subscribers')); | |
this.resolve('tracks', new BridgeCollection(Track, this.uri, 'playlist_tracks')); | |
this.resolveDone(); | |
}; | |
Playlist.prototype._popularity = function (propsMask) { | |
var load = function (data) { | |
this.resolveMany(propsMask, data); | |
}; | |
var fail = function (oops) { | |
this.resolveFail(propsMask, oops); | |
}; | |
SP.request('playlist_popularity', [this.uri], this, load, fail); | |
}; | |
Playlist._libraryListener = null; | |
Playlist.fromURI = Cache.lookup; | |
Playlist.fromURIs = Cache.lookupMany; | |
Playlist._cache = new Cache(Playlist); | |
Playlist.createTemporary = function (name) { | |
var promise = new Promise(); | |
var done = function (result) { | |
var playlist = new Playlist(result.uri); | |
Playlist._cache.cache(result.uri, playlist); | |
playlist.resolve('name', name); | |
promise.setDone(playlist); | |
}; | |
SP.request('playlist_create_temporary', [name], promise, done, promise.setFail); | |
return promise; | |
}; | |
Playlist.removeTemporary = function (playlist) { | |
var promise = new Promise(); | |
var done = function (result) { | |
Playlist._cache.remove(playlist.uri); | |
promise.setDone(); | |
}; | |
SP.request('playlist_remove_temporary', [playlist.name], promise, done, promise.setFail); | |
return promise; | |
}; | |
Playlist.create = function (name) { | |
var promise = new Promise(); | |
var done = function (result) { | |
var playlist = new Playlist(result.uri); | |
Playlist._cache.cache(result.uri, playlist); | |
playlist.resolve('name', name); | |
promise.setDone(playlist); | |
}; | |
SP.request('playlist_create', [name], promise, done, promise.setFail); | |
return promise; | |
}; | |
Playlist.getOrCreateLibraryListener = function () { | |
var listener = Playlist._libraryListener; | |
if (!listener) { | |
listener = new ProxyListener(); | |
listener.bridgeListen('library_event_wait', [exports.session.user.uri]); | |
listener.filter(function (evt) { | |
if (evt.type != 'insert' && evt.type != 'remove') | |
return; | |
var newState = evt.type == 'insert'; | |
switch (evt.receiver) { | |
case 'playlists': | |
Playlist._cache.update(evt.uris, { subscribed: newState }); | |
break; | |
case 'published': | |
Playlist._cache.update(evt.uris, { published: newState }); | |
break; | |
} | |
}); | |
Playlist._libraryListener = listener; | |
} | |
return listener; | |
}; | |
Playlist.prototype.resolveMany = function (propsMask, data, opt_silent) { | |
if (data && this._hasStaticName()) { | |
delete data.name; | |
} | |
Playlist._superClass.resolveMany.call(this, propsMask, data, opt_silent); | |
}; | |
Playlist.prototype.load = function () { | |
var args = SP.varargs(arguments); | |
if (Array.prototype.indexOf.call(args, 'subscribed') >= 0) { | |
Playlist.getOrCreateLibraryListener(); | |
} | |
var nameIndex; | |
if ((nameIndex = Array.prototype.indexOf.call(args, 'name')) !== -1 && this._hasStaticName()) { | |
var argsWithoutName = Array.prototype.slice.call(args, 0); | |
argsWithoutName.splice(nameIndex, 1); | |
var argsWithoutNamePromise = Playlist._superClass.load.apply(this, argsWithoutName); | |
var promise = new Promise(this); | |
Promise.join(argsWithoutNamePromise, this._loadStaticName()).done(function () { | |
promise.setDone(); | |
}).fail(function () { | |
promise.setFail(); | |
}); | |
return promise; | |
} else { | |
return Playlist._superClass.load.apply(this, args); | |
} | |
}; | |
Playlist._rStaticName = /^spotify:user:[^:]+:(starred|toplist|top:tracks)$/; | |
Playlist.prototype._hasStaticName = function () { | |
return Playlist._rStaticName.test(this.uri); | |
}; | |
Playlist.prototype._loadStaticName = function () { | |
var promise = new Promise(this); | |
var matches = this.uri.match(Playlist._rStaticName); | |
var type = matches && matches[1]; | |
if (type) { | |
if (type == 'top:tracks') { | |
type = 'toplist'; | |
} | |
if (!Playlist._langStrings) { | |
var self = this; | |
(function (playlistStrings) { | |
Playlist._langStrings = playlistStrings; | |
self._resolveStaticName(type); | |
promise.setDone(); | |
}(require('node_modules/api/@loc.loc/strings/playlist.lang'))); | |
} else { | |
this._resolveStaticName(type); | |
promise.setDone(); | |
} | |
} else { | |
promise.setFail('Invalid type'); | |
} | |
return promise; | |
}; | |
Playlist._stringKeyByType = { | |
starred: 'Starred', | |
toplist: 'Toplist' | |
}; | |
Playlist.prototype._resolveStaticName = function (type) { | |
var stringKey = Playlist._stringKeyByType[type]; | |
this.resolve('name', Playlist._langStrings.get(stringKey)); | |
}; | |
Playlist.prototype._observed = function () { | |
this.bridgeListen('playlist_event_wait', [this.uri]); | |
}; | |
Playlist.prototype.eventFail = function (error) { | |
if (!this._obcount) | |
this.bridgeUnlisten(); | |
Playlist._superClass.eventFail.call(this, error); | |
}; | |
Playlist.prototype.setDescription = function (description) { | |
return promisedRequest(this, 'playlist_set_description', [ | |
this.uri, | |
description | |
], true); | |
}; | |
Playlist.prototype.setImage = function (imageUrl) { | |
return promisedRequest(this, 'playlist_set_image', [ | |
this.uri, | |
imageUrl | |
], true); | |
}; | |
Playlist.prototype.setName = function (name) { | |
return promisedRequest(this, 'playlist_set_name', [ | |
this.uri, | |
name | |
], true); | |
}; | |
Playlist.prototype.setSource = function (source, link) { | |
return promisedRequest(this, 'playlist_set_source', [ | |
this.uri, | |
source, | |
link | |
]); | |
}; | |
Playlist.prototype.enforceRules = function (rules) { | |
return promisedRequest(this, 'playlist_enforce_rules', [ | |
this.uri, | |
rules | |
]); | |
}; | |
function Profile(uri) { | |
MdL.call(this); | |
this.resolve('uri', uri); | |
} | |
SP.inherit(Profile, MdL); | |
Profile.fromURI = Cache.lookup; | |
Profile.fromURIs = Cache.lookupMany; | |
Profile._cache = new Cache(Profile); | |
Loadable.define(Profile, ['uri']); | |
Loadable.define(Profile, [ | |
'artist', | |
'user' | |
], '_loadArtistOrUser'); | |
Loadable.define(Profile, [ | |
'name', | |
'image', | |
'images' | |
], '_metadata'); | |
Profile.prototype._make_artist = function (uri) { | |
return Artist.fromURI(uri); | |
}; | |
Profile.prototype._make_user = function (uri) { | |
return User.fromURI(uri); | |
}; | |
Profile.prototype._loadArtistOrUser = function (propsMask) { | |
var object = exports.fromURI(this.uri), promise; | |
if (object instanceof Artist) { | |
promise = object.load('user').done(this, function () { | |
var uri = object.user ? object.user.uri : null; | |
this.resolveMany(propsMask, { | |
artist: object.uri, | |
user: uri | |
}); | |
}); | |
} else if (object instanceof User) { | |
promise = object.load('artist').done(this, function () { | |
var uri = object.artist ? object.artist.uri : null; | |
this.resolveMany(propsMask, { | |
artist: uri, | |
user: object.uri | |
}); | |
}); | |
} else { | |
throw new Error('Invalid URI for Profile'); | |
} | |
promise.fail(this, function () { | |
this.resolveFail(propsMask, { message: 'Failed to resolve artist/user objects' }); | |
}); | |
}; | |
Profile.prototype._metadata = function (propsMask) { | |
this.load('artist', 'user').done(this, function () { | |
if (this.user) { | |
this.user.load('name', 'username', 'image', 'images').done(this, function () { | |
var data = { | |
name: this.user.name, | |
image: this.user.image, | |
images: this.user.images | |
}; | |
var nameDefined = data.name && data.name.toLowerCase() !== this.user.username, imageUploaded = data.image; | |
if (!this.artist || nameDefined && imageUploaded) | |
return this.resolveMany(propsMask, data); | |
this.artist.load('name', 'image', 'images').done(this, function (artist) { | |
if (!nameDefined) | |
data.name = artist.name; | |
if (!imageUploaded) { | |
data.image = artist.image; | |
data.images = artist.images; | |
} | |
}).always(this, function () { | |
this.resolveMany(propsMask, data); | |
}); | |
}).fail(this, function (_, error) { | |
this.resolveFail(propsMask, error); | |
}); | |
} else { | |
this.artist.load('name', 'image', 'images').done(this, function (artist) { | |
this.resolveMany(propsMask, { | |
name: artist.name, | |
image: artist.image, | |
images: artist.images | |
}); | |
}).fail(this, function (_, error) { | |
this.resolveFail(propsMask, error); | |
}); | |
} | |
}); | |
}; | |
Playlist._playlistEventWait = function () { | |
SP.request('playlist_event_wait_any', [], this, this._playlistEventDone, this._playlistEventFail); | |
}; | |
Playlist._playlistEventDone = function (event) { | |
var playlist = Playlist.fromURI(event.data.uri); | |
playlist.resolveMany(0, event.data); | |
playlist.dispatchEvent(event); | |
this._playlistEventWait(); | |
}; | |
Playlist._playlistEventFail = function (error) { | |
if (error.error == 'timeout') | |
this._playlistEventWait(); | |
}; | |
function Reference(index, uri) { | |
this.index = index; | |
this.uri = uri; | |
} | |
function Session() { | |
BridgeLoadable.call(this); | |
this.resolve('user', User.fromURI('spotify:user:@')); | |
} | |
SP.inherit(Session, BridgeLoadable); | |
Loadable.define(Session, ['user']); | |
Loadable.define(Session, [ | |
'catalogue', | |
'connecting', | |
'connection', | |
'country', | |
'developer', | |
'device', | |
'incognito', | |
'language', | |
'online', | |
'partner', | |
'product', | |
'resolution', | |
'streaming', | |
'testGroup', | |
'capabilities' | |
], '_query'); | |
Session.prototype._observed = function () { | |
this.bridgeListen('session_event_wait', []); | |
}; | |
Session.prototype._query = function (propsMask) { | |
var load = function (data) { | |
this.resolveMany(propsMask, data); | |
}; | |
var fail = function (oops) { | |
this.resolveFail(propsMask, oops); | |
}; | |
SP.request('session_query', [], this, load, fail); | |
}; | |
Session.prototype.testGroupForTest = function (name) { | |
var promise = new Promise(); | |
SP.request('session_test_group', [name], promise, function (result) { | |
this.setDone(result.testGroup); | |
}, Promise.setFail); | |
return promise; | |
}; | |
function Snapshot(collection, opt_start, opt_length, opt_raw) { | |
Loadable.call(this); | |
this._collection = collection; | |
this._off = opt_start === undefined ? 0 : opt_start; | |
this._len = opt_length === undefined ? -1 : opt_length; | |
this._raw = !!opt_raw; | |
} | |
SP.inherit(Snapshot, Loadable); | |
Loadable.define(Snapshot, [ | |
'length', | |
'range' | |
], '_request'); | |
Snapshot.prototype._request = function () { | |
var col = this._collection; | |
col._snapshot(col.descriptor, this._off, this._len, this._raw).done(this, function (result) { | |
this._uris = result.array; | |
this._meta = result.metadata || []; | |
this.resolve('length', result.length); | |
this.resolve('range', { | |
offset: this._off, | |
length: this._uris.length | |
}); | |
this.resolveDone(); | |
}).fail(this, function (error) { | |
var propsMask = this._neededForLoad([ | |
'length', | |
'range' | |
]); | |
this.resolveFail(propsMask, error); | |
}); | |
}; | |
Snapshot.prototype.get = function (index) { | |
if (index instanceof Reference) | |
index = index.index; | |
index -= this._off; | |
if (index < 0 || index > this._uris.length) | |
return null; | |
return this._collection._factory(this._uris[index], this._meta[index]); | |
}; | |
Snapshot.prototype.find = function (item, first) { | |
if (first instanceof Reference) | |
first = first.index; | |
var index = this._uris.indexOf(item.uri, first || 0); | |
return index == -1 ? null : new Reference(index + this._off, this._uris[index]); | |
}; | |
Snapshot.prototype.loadAll = function () { | |
var promises = [], items = this.toArray(); | |
for (var i = 0, len = items.length; i < len; i++) { | |
var item = items[i]; | |
promises.push(item.load.apply(item, arguments)); | |
} | |
return Promise.join(promises); | |
}; | |
Snapshot.prototype.ref = function (index) { | |
var item = this.get(index); | |
return new Reference(index, item ? item.uri : null); | |
}; | |
Snapshot.prototype.toArray = function () { | |
var array = [], col = this._collection; | |
for (var i = 0, l = this._uris.length; i < l; i++) | |
array[i] = col._factory(this._uris[i], this._meta[i]); | |
return array; | |
}; | |
Snapshot.prototype.toURIs = function () { | |
return this._uris.slice(); | |
}; | |
function Track(uri) { | |
MdL.call(this); | |
this.resolve('uri', uri); | |
} | |
SP.inherit(Track, MdL); | |
Loadable.define(Track, ['uri']); | |
Loadable.define(Track, [ | |
'ad_metadata', | |
'advertisement', | |
'album', | |
'artists', | |
'availability', | |
'disc', | |
'duration', | |
'explicit', | |
'image', | |
'images', | |
'local', | |
'name', | |
'number', | |
'placeholder', | |
'playable', | |
'popularity', | |
'starred' | |
], '_metadata'); | |
MdL.init(Track, 'track'); | |
Track.prototype._make_album = function (value) { | |
return value && Album.fromURI(value.uri, value); | |
}; | |
Track.prototype._make_artists = function (value) { | |
return value && value.map(_artists); | |
}; | |
Track.fromURI = Cache.lookup; | |
Track.fromURIs = Cache.lookupMany; | |
Track._cache = new Cache(Track); | |
Track.prototype.star = function () { | |
return promisedRequest(this, 'library_star', [ | |
exports.session.user.uri, | |
this.uri | |
]); | |
}; | |
Track.prototype.unstar = function () { | |
return promisedRequest(this, 'library_unstar', [ | |
exports.session.user.uri, | |
this.uri | |
]); | |
}; | |
Track._trackEventWait = function () { | |
SP.request('track_event_wait_any', [], this, this._trackEventDone, this._trackEventFail); | |
}; | |
Track._trackEventDone = function (event) { | |
var track = Track.fromURI(event.data.uri); | |
track.resolveMany(0, event.data); | |
track.dispatchEvent(event); | |
this._trackEventWait(); | |
}; | |
Track._trackEventFail = function (error) { | |
if (error.error == 'timeout') | |
this._trackEventWait(); | |
}; | |
function User(uri) { | |
MdL.call(this); | |
this.resolve('uri', uri); | |
} | |
SP.inherit(User, MdL); | |
Loadable.define(User, ['uri']); | |
Loadable.define(User, [ | |
'currentUser', | |
'identifier', | |
'image', | |
'images', | |
'name', | |
'subscribed', | |
'username' | |
], '_metadata'); | |
Loadable.define(User, ['artist'], '_associatedArtist'); | |
MdL.init(User, 'user'); | |
User.prototype._associatedArtist = function (propsMask) { | |
var load = function (data) { | |
this.resolveMany(propsMask, data); | |
}; | |
var fail = function (oops) { | |
this.resolveFail(propsMask, oops); | |
}; | |
SP.request('user_associated_artist', [this.uri], this, load, fail); | |
}; | |
User.prototype._make_artist = function (value) { | |
return value && Artist.fromURI(value); | |
}; | |
User._relationsListener = null; | |
User.fromURI = Cache.lookup; | |
User.fromURIs = Cache.lookupMany; | |
User._cache = new Cache(User); | |
User.fromUsername = function (username) { | |
var escaped = encodeURIComponent(username), i = -1; | |
while ((i = escaped.indexOf('%', i + 1)) > -1) { | |
escaped = escaped.substring(0, i + 1) + escaped.substring(i + 1, i + 3).toLowerCase() + escaped.substring(i + 3); | |
} | |
return User.fromURI('spotify:user:' + escaped); | |
}; | |
User.getOrCreateRelationsListener = function () { | |
var listener = User._relationsListener; | |
if (!listener) { | |
listener = new ProxyListener(); | |
listener.bridgeListen('relations_event_wait', [exports.session.user.uri]); | |
listener.filter(function (evt) { | |
if (evt.receiver != 'subscriptions' || evt.type != 'add' && evt.type != 'remove') | |
return; | |
User._cache.update(evt.uris, { subscribed: evt.type == 'add' }); | |
}); | |
User._relationsListener = listener; | |
} | |
return listener; | |
}; | |
User.prototype.load = function () { | |
var args = SP.varargs(arguments); | |
if (Array.prototype.indexOf.call(args, 'subscribed') >= 0) { | |
User.getOrCreateRelationsListener(); | |
} | |
return User._superClass.load.apply(this, args); | |
}; | |
var fromURI = function (uri, opt_data) { | |
var parts = uri.split(':'); | |
var result = null; | |
switch (parts[1]) { | |
case 'album': | |
if (parts.length == 4) | |
result = Disc.fromURI(uri, opt_data); | |
else if (parts.length == 3) | |
result = Album.fromURI(uri, opt_data); | |
break; | |
case 'artist': | |
if (parts.length == 3) | |
result = Artist.fromURI(uri, opt_data); | |
break; | |
case 'track': | |
if (parts.length == 3) | |
result = Track.fromURI(uri, opt_data); | |
break; | |
case 'local': | |
if (parts.length === 6) | |
result = Track.fromURI(uri, opt_data); | |
else if (parts.length === 4) | |
result = Album.fromURI(uri, opt_data); | |
else if (parts.length === 3) | |
result = Artist.fromURI(uri, opt_data); | |
break; | |
case 'user': | |
if (parts.length > 3 && parts[3] == 'collection') | |
return new BridgeCollection(Track, uri, 'library_tracks'); | |
if (parts.length > 3 && parts.length <= 5 && parts[2] != 'facebook') | |
result = Playlist.fromURI(uri, opt_data); | |
else if (parts.length == 3) | |
result = User.fromURI(uri, opt_data); | |
break; | |
} | |
return result; | |
}; | |
Playlist._playlistEventWait(); | |
Track._trackEventWait(); | |
exports.Observable = Observable; | |
exports.Loadable = Loadable; | |
exports.BridgeLoadable = BridgeLoadable; | |
exports.MdL = MdL; | |
exports.Album = Album; | |
exports.Application = Application; | |
exports.Artist = Artist; | |
exports.Cache = Cache; | |
exports.Client = Client; | |
exports.Collection = Collection; | |
exports.BridgeCollection = BridgeCollection; | |
exports.Context = Context; | |
exports.Disc = Disc; | |
exports.Group = Group; | |
exports.ListDescriptor = ListDescriptor; | |
exports.Player = Player; | |
exports.Playlist = Playlist; | |
exports.Profile = Profile; | |
exports.Promise = Promise; | |
exports.Session = Session; | |
exports.Track = Track; | |
exports.User = User; | |
exports.application = new Application(); | |
exports.client = new Client(); | |
exports.fromURI = fromURI; | |
exports.player = new Player('main'); | |
exports.preview = new Player('preview'); | |
exports.promisedRequest = promisedRequest; | |
exports.session = new Session(); | |
}, | |
'scripts/player.widgets.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models, utils, Marquee) { | |
function ArtistWidget(element, initialSize, eventManager, logger) { | |
var _this = this; | |
this.element = element; | |
this.eventManager = eventManager; | |
this.track = null; | |
this.artistsWanted = 0; | |
this.artistsAdded = 0; | |
this.logger = logger; | |
this.marquee = new Marquee(element, initialSize); | |
this.marquee.init(); | |
this.eventManager.subscribe(this.eventManager.Events.TRACK_CHANGED, this.setTrack, this); | |
} | |
ArtistWidget.prototype.init = function () { | |
this.onArtistLoaded = this.onArtistLoaded.bind(this); | |
this.onArtistClicked = this.onArtistClicked.bind(this); | |
utils.addEventSimple(this.element, 'click', this.onArtistClicked); | |
}; | |
ArtistWidget.prototype.resize = function (width) { | |
this.marquee.widthAdjust(width); | |
}; | |
ArtistWidget.prototype.onArtistClicked = function (e) { | |
e.preventDefault(); | |
if (!e.target.href) { | |
return; | |
} | |
if (this.track.advertisement) { | |
this.eventManager.trigger(this.eventManager.Events.AD_CLICKED); | |
} else { | |
this.logger.userHit('arist_link', { artist_id: e.target.href.toSpotifyURI() }); | |
models.application.openURI(e.target.href.toSpotifyURI()); | |
} | |
}; | |
ArtistWidget.prototype.onArtistLoaded = function (artist) { | |
this.addArtist(artist); | |
}; | |
ArtistWidget.prototype.setTrack = function (track) { | |
var self = this; | |
track.load('artists').done(function (track) { | |
self.clear(); | |
self.track = track; | |
self.artistsWanted = track.artists && track.artists.length || 0; | |
var size = self.artistsWanted; | |
for (var i = 0; i < size; i++) { | |
track.artists[i].load([ | |
'name', | |
'uri' | |
]).done(function (artist) { | |
self.onArtistLoaded(artist); | |
}); | |
} | |
}); | |
}; | |
ArtistWidget.prototype.addArtist = function (artist) { | |
if (this.artistsAdded > 0) { | |
this.element.appendChild(document.createTextNode(', ')); | |
} | |
var a = document.createElement('a'); | |
a.innerHTML = artist.name.decodeForHtml(); | |
a.href = artist.uri.toSpotifyURL(); | |
this.element.appendChild(a); | |
this.artistsAdded++; | |
if (this.artistsAdded === this.artistsWanted) { | |
this.marquee.refresh(); | |
} | |
}; | |
ArtistWidget.prototype.clear = function () { | |
if (this.track === null) { | |
return; | |
} | |
this.track = null; | |
this.element.innerHTML = ''; | |
this.artistsAdded = 0; | |
}; | |
exports.ArtistWidget = ArtistWidget; | |
}(require('node_modules/api/scripts/models.js'), require('scripts/player-utils.js').playerUtils, require('scripts/player-utils.js').Marquee)); | |
}, | |
'scripts/player.shufflebutton.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models, pu) { | |
var playerUtils = pu.playerUtils; | |
var playerStorage = pu.playerStorage; | |
models.Loadable.define(models.Player, ['__rules'], '_playapp'); | |
function ShuffleButton(domID, logger, adBreak) { | |
var self = this; | |
var player = models.player; | |
self._node = document.getElementById(domID); | |
self._store = new playerStorage(); | |
self._adBreak = adBreak; | |
self.logger = logger; | |
player.load(['shuffle']).done(function (player) { | |
self._player = player; | |
self.init(); | |
}); | |
} | |
ShuffleButton.prototype.init = function () { | |
var self = this; | |
self._player.addEventListener('change:shuffle', function () { | |
self.updateStatus(); | |
}); | |
playerUtils.addEventSimple(self._node, 'click', function () { | |
self.toggle(); | |
self.logger.userHit('shuffle_button', { track_id: self._player.track.uri }); | |
}); | |
self._player.addEventListener('change', function () { | |
self._handleRules(); | |
}); | |
if (self._store.get('spShuffle') === 'true') { | |
self.on(); | |
} | |
}; | |
ShuffleButton.prototype._handleRules = function () { | |
var rules = this._player.__rules; | |
if (!rules.shuffle || this._adBreak.inProgress()) { | |
this.disableButton(); | |
} else { | |
this.enableButton(); | |
} | |
}; | |
ShuffleButton.prototype.updateStatus = function () { | |
if (this._player.shuffle) { | |
playerUtils.addClass(this._node, 'active'); | |
this._store.set('spShuffle', 'true'); | |
} else { | |
playerUtils.removeClass(this._node, 'active'); | |
if (this._player.__rules && this._player.__rules.shuffle) { | |
this._store.set('spShuffle', 'false'); | |
} | |
} | |
}; | |
ShuffleButton.prototype.on = function () { | |
playerUtils.addClass(this._node, 'active'); | |
var self = this; | |
self._player.setShuffle(true).done(function () { | |
self.updateStatus(); | |
}); | |
}; | |
ShuffleButton.prototype.off = function () { | |
playerUtils.removeClass(this._node, 'active'); | |
var self = this; | |
self._player.setShuffle(false).done(function () { | |
self.updateStatus(); | |
}); | |
}; | |
ShuffleButton.prototype.toggle = function () { | |
var self = this; | |
if (playerUtils.hasClass(self._node, 'disabled')) { | |
return false; | |
} | |
if (!self._player.shuffle) { | |
self.on(); | |
} else { | |
self.off(); | |
} | |
}; | |
ShuffleButton.prototype.disableButton = function () { | |
/*beej: | |
playerUtils.addClass(this._node, 'disabled'); | |
this.updateStatus(); | |
*/ | |
}; | |
ShuffleButton.prototype.enableButton = function () { | |
playerUtils.removeClass(this._node, 'disabled'); | |
this.updateStatus(); | |
}; | |
exports.ShuffleButton = ShuffleButton; | |
}(require('node_modules/api/scripts/models.js'), require('scripts/player-utils.js'))); | |
}, | |
'scripts/player.repeatbutton.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models, pu) { | |
var playerUtils = pu.playerUtils; | |
var playerStorage = pu.playerStorage; | |
models.Loadable.define(models.Player, ['__rules'], '_playapp'); | |
function RepeatButton(domID, logger, adBreak) { | |
var self = this; | |
var player = models.player; | |
self._node = document.getElementById(domID); | |
self._store = new playerStorage(); | |
self._disabled = false; | |
self._adBreak = adBreak; | |
self.logger = logger; | |
player.load(['repeat']).done(function (player) { | |
self._player = player; | |
self.init(domID); | |
}); | |
} | |
RepeatButton.prototype.init = function () { | |
var self = this; | |
self._player.addEventListener('change:repeat', function () { | |
self.updateStatus(); | |
}); | |
self._player.addEventListener('change', function () { | |
self._handleRules(); | |
}); | |
playerUtils.addEventSimple(self._node, 'click', function () { | |
self.toggle(); | |
self.logger.userHit('repeat_button', { track_id: self._player.track.uri }); | |
}); | |
if (self._store.get('spRepeat') === 'true') { | |
self.on(); | |
} | |
}; | |
RepeatButton.prototype._handleRules = function () { | |
var rules = this._player.__rules; | |
if (!rules.repeat || this._adBreak.inProgress()) { | |
this.disableButton(); | |
} else { | |
this.enableButton(); | |
} | |
}; | |
RepeatButton.prototype.updateStatus = function () { | |
if (this._player.repeat) { | |
playerUtils.addClass(this._node, 'active'); | |
this._store.set('spRepeat', 'true'); | |
} else { | |
playerUtils.removeClass(this._node, 'active'); | |
if (this._player.__rules && this._player.__rules.repeat) { | |
this._store.set('spRepeat', 'false'); | |
} | |
} | |
}; | |
RepeatButton.prototype.on = function () { | |
var self = this; | |
playerUtils.addClass(this._node, 'active'); | |
self._player.setRepeat(true).done(function () { | |
self.updateStatus(); | |
}); | |
}; | |
RepeatButton.prototype.off = function () { | |
playerUtils.removeClass(this._node, 'active'); | |
var self = this; | |
self._player.setRepeat(false).done(function () { | |
self.updateStatus(); | |
}); | |
}; | |
RepeatButton.prototype.toggle = function () { | |
if (playerUtils.hasClass(this._node, 'disabled')) { | |
return false; | |
} | |
if (!this._player.repeat) { | |
this.on(); | |
} else { | |
this.off(); | |
} | |
}; | |
RepeatButton.prototype.disableButton = function () { | |
/*beej: | |
this._disabled = true; | |
playerUtils.addClass(this._node, 'disabled'); | |
this.updateStatus(); | |
*/ | |
}; | |
RepeatButton.prototype.enableButton = function () { | |
this._disabled = false; | |
playerUtils.removeClass(this._node, 'disabled'); | |
this.updateStatus(); | |
}; | |
exports.RepeatButton = RepeatButton; | |
}(require('node_modules/api/scripts/models.js'), require('scripts/player-utils.js'))); | |
}, | |
'scripts/player.playpausebutton.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models, pu) { | |
var playerUtils = pu.playerUtils; | |
function PlayPauseButton(domID, logger, adBreak) { | |
var self = this; | |
var player = models.player; | |
self.logger = logger; | |
self._node = document.getElementById(domID); | |
self._adBreak = adBreak; | |
self._disabled = false; | |
player.load([ | |
'playing', | |
'track', | |
'context' | |
]).done(function (player) { | |
self._player = player; | |
self.init(); | |
}); | |
} | |
PlayPauseButton.prototype.init = function () { | |
var self = this; | |
playerUtils.addEventSimple(self._node, 'click', function (e) { | |
self.toggle(e); | |
}); | |
self._player.addEventListener('change:playing', function () { | |
self.updateStatus(); | |
}); | |
self._player.addEventListener('change:track', function () { | |
self.updateStatus(); | |
}); | |
self._adBreak.addEventListener('updateDetails', function () { | |
self.updateStatus(); | |
}); | |
self.updateStatus(); | |
}; | |
PlayPauseButton.prototype.toggle = function (e) { | |
if (this._disabled) { | |
return false; | |
} | |
if (e) { | |
e.preventDefault(); | |
} | |
if (this.isPlaying()) { | |
if (this._adBreak.inProgress()) { | |
this._adBreak.pause(); | |
} else { | |
this._player.pause(); | |
} | |
} else { | |
if (this._adBreak.inProgress()) { | |
this._adBreak.resume(); | |
} else { | |
this._player.play(); | |
} | |
} | |
this.logger.userHit('play_pause', { track_id: this._player.track.uri }); | |
}; | |
PlayPauseButton.prototype.isPlaying = function () { | |
var adBreakDetails = this._adBreak.getDetails(); | |
return adBreakDetails ? adBreakDetails.playing : this._player.playing; | |
}; | |
PlayPauseButton.prototype.updateStatus = function () { | |
if (this.isPlaying()) { | |
playerUtils.addClass(this._node, 'playing'); | |
} else { | |
playerUtils.removeClass(this._node, 'playing'); | |
} | |
if (!this._adBreak.inProgress() && !this._player.track && !(this._player.context && this._player.context.uri)) { | |
this.disableButton(); | |
} else { | |
this.enableButton(); | |
} | |
this.setPageTitle(); | |
}; | |
PlayPauseButton.prototype.setPageTitle = function () { | |
var newTitle; | |
var currentTrack = this._player.track; | |
if (!currentTrack) { | |
newTitle = 'Spotify Web Player'; | |
} else { | |
newTitle = currentTrack.name + ' - ' + (currentTrack.artists && currentTrack.artists[0].name) || ''; | |
} | |
newTitle = (this._player.playing ? '\u25B6 ' : '') + newTitle; | |
models.application.setTitle(newTitle); | |
}; | |
PlayPauseButton.prototype.disableButton = function () { | |
/*beej: | |
this._disabled = true; | |
playerUtils.addClass(this._node, 'disabled'); | |
playerUtils.removeClass(this._node, 'playing'); | |
*/ | |
}; | |
PlayPauseButton.prototype.enableButton = function () { | |
this._disabled = false; | |
playerUtils.removeClass(this._node, 'disabled'); | |
}; | |
exports.PlayPauseButton = PlayPauseButton; | |
}(require('node_modules/api/scripts/models.js'), require('scripts/player-utils.js'))); | |
}, | |
'scripts/player.nextbackbuttons.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models, pu) { | |
var playerUtils = pu.playerUtils; | |
var fastSkipTimer = null; | |
var player = models.player; | |
models.Loadable.define(models.Player, ['__rules'], '_playapp'); | |
function NextButton(domID, eventManager, logger, adBreak) { | |
var self = this; | |
self._node = document.getElementById(domID); | |
self._eventManager = eventManager; | |
self._adBreak = adBreak; | |
self.logger = logger; | |
self._disabled = false; | |
player.load(['playing']).done(function (player) { | |
self._player = player; | |
self.init(); | |
}); | |
} | |
NextButton.prototype.init = function () { | |
var self = this; | |
playerUtils.addEventSimple(self._node, 'click', function (e) { | |
e.preventDefault(); | |
self.playerNext(); | |
self._node.blur(); | |
}); | |
self._player.addEventListener('change', function () { | |
self._handleRules(); | |
}); | |
}; | |
NextButton.prototype._handleRules = function () { | |
if (this._adBreak.inProgress()) { | |
this.disableButton(); | |
return; | |
} | |
var rules = this._player.__rules; | |
if (!rules.next) { | |
this.disableButton(); | |
return; | |
} | |
if (!this._player.track || !this._player.context) { | |
this.disableButton(); | |
return; | |
} | |
if (this._player.repeat === true) { | |
this.enableButton(); | |
return; | |
} | |
this.enableButton(); | |
}; | |
NextButton.prototype.playerNext = function () { | |
debugger; | |
if (!this._disabled) { | |
this._eventManager.trigger(this._eventManager.Events.TRACK_SKIPPED); | |
this.logger.userHit('next_button', { track_id: this._player.track.uri }); | |
this._player.skipToNextTrack(); | |
} | |
}; | |
NextButton.prototype.disableButton = function () { | |
/*beej: | |
this._disabled = true; | |
playerUtils.addClass(this._node, 'disabled'); | |
*/ | |
}; | |
NextButton.prototype.enableButton = function () { | |
this._disabled = false; | |
playerUtils.removeClass(this._node, 'disabled'); | |
}; | |
NextButton.prototype.update = function () { | |
this._handleRules(); | |
}; | |
function BackButton(domID, eventManager, logger, adBreak) { | |
var self = this; | |
self._eventManager = eventManager; | |
self._disabled = false; | |
self._node = document.getElementById(domID); | |
self._adBreak = adBreak; | |
self.logger = logger; | |
player.load(['playing']).done(function (player) { | |
self._player = player; | |
self.init(); | |
}); | |
} | |
BackButton.prototype.init = function () { | |
var self = this; | |
playerUtils.addEventSimple(self._node, 'click', function (e) { | |
e.preventDefault(); | |
self.playerBack(); | |
self._node.blur(); | |
}); | |
self._player.addEventListener('change', function () { | |
self._handleRules(); | |
}); | |
}; | |
BackButton.prototype._handleRules = function () { | |
if (this._adBreak.inProgress()) { | |
this.disableButton(); | |
return; | |
} | |
var rules = this._player.__rules; | |
if (!rules.previous) { | |
this.disableButton(); | |
return; | |
} else { | |
if (this._disabled) { | |
this.enableButton(); | |
} | |
} | |
if (!this._player.track || !this._player.context) { | |
this.disableButton(); | |
return; | |
} else { | |
this.enableButton(); | |
} | |
}; | |
BackButton.prototype.playerBack = function () { | |
/*beej: | |
if (this._disabled || this._player.track.duration - this._player.position < 500) { | |
return false; | |
}*/ | |
this.logger.userHit('previous_button', { track_id: this._player.track.uri }); | |
this._eventManager.trigger(this._eventManager.Events.BACK_BUTTON_PRESS_START); | |
if (this._player.position > 4000 || this._player.__index === 0 && !this._player.repeat) { | |
this._player.seek(0); | |
if (!this._player.playing) { | |
this._player.play(); | |
} | |
} else { | |
this._player.skipToPrevTrack(); | |
this._eventManager.trigger(this._eventManager.Events.TRACK_SKIPPED); | |
} | |
this._eventManager.trigger(this._eventManager.Events.BACK_BUTTON_PRESS_COMPLETE); | |
}; | |
BackButton.prototype.disableButton = function () { | |
/*beej: | |
this._disabled = true; | |
playerUtils.addClass(this._node, 'disabled'); | |
*/ | |
}; | |
BackButton.prototype.enableButton = function () { | |
this._disabled = false; | |
playerUtils.removeClass(this._node, 'disabled'); | |
}; | |
BackButton.prototype.update = function () { | |
this._handleRules(); | |
}; | |
exports.NextButton = NextButton; | |
exports.BackButton = BackButton; | |
}(require('node_modules/api/scripts/models.js'), require('scripts/player-utils.js'))); | |
}, | |
'scripts/player.progressbar.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models, pu, Slider) { | |
var playerUtils = pu.playerUtils; | |
var player = models.player; | |
models.Loadable.define(models.Player, ['__uid'], '_playapp'); | |
function ProgressBar(wrapperID, eventManager, logger, adBreak) { | |
var self = this; | |
self._wrapper = document.getElementById(wrapperID); | |
self._eventManager = eventManager; | |
self._timer = null; | |
self._updateInterval = 310; | |
self._control = null; | |
self._isScrubbing = false; | |
self._seekingTo = null; | |
self._timeMarker = null; | |
self._timeMarkerCenter = 0; | |
self._currentTrackUID = -1; | |
self.logger = logger; | |
self._maxContainerWidth = null; | |
self._timeMarkerWidth = null; | |
self._adBreak = adBreak; | |
player.load([ | |
'playing', | |
'track', | |
'position', | |
'duration' | |
]).done(function (player) { | |
self._player = player; | |
self._currentTrackUID = player.__uid; | |
self.init(); | |
}); | |
} | |
ProgressBar.prototype.init = function () { | |
var self = this; | |
self.buildNodes(); | |
self._control = new Slider(self._barClickArea, self._scrubber, { | |
steps: self._player.track ? self._player.track.duration : 100, | |
onStart: function () { | |
self._isScrubbing = true; | |
playerUtils.addClass(self._timeMarker, 'active'); | |
playerUtils.addClass(self._timeMarkerArrow, 'active'); | |
}, | |
onChange: function () { | |
if (self._player.track) { | |
setTimeout(function () { | |
self.updateElapsedTime(this.step); | |
var handleCentre = this.handle.offsetLeft + this.handle.offsetWidth / 2; | |
self.moveTimeMarker(handleCentre); | |
self._barInner.style.width = handleCentre + 'px'; | |
}.bind(this), 1); | |
} | |
}, | |
onComplete: function () { | |
if (self._player.track && self._player.track.duration - this.step <= 500) { | |
self._player.skipToNextTrack(); | |
} else { | |
self._player.seek(this.step); | |
self._seekingTo = this.step; | |
self._barInner.style.width = this.handle.offsetLeft + this.handle.offsetWidth / 2 + 'px'; | |
} | |
self._isScrubbing = false; | |
self.logger.userHit('seek_bar', { | |
track_id: self._player.track.uri, | |
seek_ms: this.step | |
}); | |
playerUtils.removeClass(self._timeMarker, 'active'); | |
playerUtils.removeClass(self._timeMarkerArrow, 'active'); | |
self.pauseTimer(600); | |
} | |
}); | |
self._player.addEventListener('change:track', function () { | |
self._handleTrackChange(); | |
}); | |
self._player.addEventListener('change', function () { | |
if (self._currentTrackUID !== player.__uid) { | |
self._handleTrackChange(); | |
} | |
}); | |
self._eventManager.subscribe(self._eventManager.Events.TRACK_SKIPPED, function () { | |
self._handleTrackChange(); | |
}); | |
self._eventManager.subscribe(self._eventManager.Events.BACK_BUTTON_PRESS_START, function () { | |
self._handleTrackChange(); | |
}); | |
self._player.addEventListener('change:playing', function () { | |
if (self._player.playing) { | |
self.runTimer(); | |
} else { | |
self.stopTimer(); | |
} | |
}); | |
self._adBreak.addEventListener('startBreak', self._handleAdBreakStartEnd.bind(self)); | |
self._adBreak.addEventListener('endBreak', self._handleAdBreakStartEnd.bind(self)); | |
self._adBreak.addEventListener('updateDetails', self._handleAdBreakUpdate.bind(self)); | |
self.moveTimeMarker(0); | |
self.runTimer(); | |
}; | |
ProgressBar.prototype.moveTimeMarker = function (handlePos) { | |
var self = this; | |
var getArrowPosition = function (pos) { | |
if (pos < self._timeMarkerArrowWidth) { | |
pos = self._timeMarkerArrowWidth; | |
} else if (pos > self._maxContainerWidth - self._timeMarkerArrowWidth - 2) { | |
pos = self._maxContainerWidth - self._timeMarkerArrowWidth - 2; | |
} | |
pos = pos - self._timeMarkerArrowWidth / 2 + 1; | |
return pos; | |
}; | |
var arrowPos = getArrowPosition(handlePos); | |
var movingTo = handlePos - self._timeMarkerCenter; | |
if (movingTo < 2) { | |
movingTo = 2; | |
} else if (handlePos > self._maxContainerWidth - self._timeMarkerCenter - 2) { | |
movingTo = self._maxContainerWidth - self._timeMarkerWidth - 2; | |
} | |
self._timeMarkerArrow.style.left = arrowPos + 'px'; | |
self._timeMarker.style.left = movingTo + 'px'; | |
}; | |
ProgressBar.prototype.setPageTitle = function (time, initialSet) { | |
var newTitle; | |
var currentTrack = this._player.track; | |
var adDetails = this._adBreak.getDetails(); | |
if (adDetails) { | |
newTitle = adDetails.title + ' - ' + adDetails.description; | |
} else if (!currentTrack) { | |
newTitle = ''; | |
} else { | |
newTitle = currentTrack.name + ' - ' + (currentTrack.artists && currentTrack.artists[0].name) || ''; | |
} | |
if (!adDetails && (this._player.playing || time && initialSet)) { | |
newTitle = '[' + playerUtils.secsToMins((time || this._player.position) / 1000) + '] ' + newTitle; | |
} | |
models.application.setTitle(newTitle); | |
}; | |
ProgressBar.prototype.buildNodes = function () { | |
this._barClickArea = document.createElement('div'); | |
this._barClickArea.id = 'bar-click'; | |
this._barOuter = document.createElement('div'); | |
this._barOuter.id = 'bar-outer'; | |
this._barInner = document.createElement('div'); | |
this._barInner.id = 'bar-inner'; | |
this._scrubber = document.createElement('span'); | |
this._scrubber.id = 'position'; | |
this._barClickArea.appendChild(this._barOuter); | |
this._barOuter.appendChild(this._barInner); | |
this._barOuter.appendChild(this._scrubber); | |
this._wrapper.appendChild(this._barClickArea); | |
this._timeWrapper = document.createElement('div'); | |
this._timeWrapper.id = 'time'; | |
this._trackCurrentPos = document.createElement('span'); | |
this._trackCurrentPos.id = 'track-current'; | |
this._trackLength = document.createElement('span'); | |
this._trackLength.id = 'track-length'; | |
this._timeMarker = document.createElement('div'); | |
this._timeMarker.id = 'time-marker'; | |
this._timeMarkerText = document.createElement('div'); | |
this._timeMarkerText.id = 'time-marker-text'; | |
this._timeMarkerArrow = document.createElement('span'); | |
this._timeMarkerArrow.id = 'time-marker-arrow'; | |
this._timeMarker.appendChild(this._timeMarkerText); | |
this._timeMarkerText.appendChild(this._trackCurrentPos); | |
this._timeMarkerText.appendChild(this._trackLength); | |
var duration = (this._player.track && this._player.track.duration || 0) / 1000; | |
this._trackLength.textContent = playerUtils.secsToMins(duration); | |
this._wrapper.appendChild(this._timeMarker); | |
this._wrapper.appendChild(this._timeMarkerArrow); | |
this._wrapper.appendChild(this._timeWrapper); | |
var self = this; | |
setTimeout(function () { | |
self.setupMarkerSizing(); | |
}, 10); | |
}; | |
ProgressBar.prototype.setupMarkerSizing = function () { | |
this._timeMarkerWidth = this._timeMarker.offsetWidth; | |
this._timeMarkerCenter = this._timeMarkerWidth / 2; | |
this._timeMarkerArrowWidth = this._timeMarkerArrow.offsetWidth; | |
this._timeMarkerDefaultPos = this._timeMarkerCenter + this._timeMarkerArrowWidth / 2; | |
this._maxContainerWidth = this._barOuter.offsetWidth; | |
this._maxMarkerRightPos = this._maxContainerWidth - this._timeMarkerWidth - 2; | |
this._maxArrowRightPos = this._maxContainerWidth + this._timeMarkerArrowWidth - this._timeMarkerWidth - 2; | |
}; | |
ProgressBar.prototype._handleTrackChange = function () { | |
var self = this; | |
if (!self._player.track) { | |
self.stopTimer(); | |
return false; | |
} | |
self._trackLength.textContent = playerUtils.secsToMins(self._player.track.duration / 1000); | |
self.updateElapsedTime(0); | |
self._seekingTo = null; | |
self._control.jumpToStep(0, false); | |
self._control.setSteps(self._player.track.duration); | |
self._currentTrackUID = player.__uid || -1; | |
self.pauseTimer(1500); | |
self.setupMarkerSizing(); | |
}; | |
ProgressBar.prototype._handleAdBreakStartEnd = function () { | |
var self = this; | |
if (self._adBreak.inProgress()) { | |
self.disable(); | |
} | |
}; | |
ProgressBar.prototype._handleAdBreakUpdate = function () { | |
var self = this; | |
var d = self._adBreak.getDetails(); | |
if (d && d.duration > 0) { | |
self._trackLength.textContent = playerUtils.secsToMins(d.duration / 1000); | |
self._control.setSteps(d.duration); | |
self._control.jumpToStep(d.position, false); | |
self.updateElapsedTime(d.position); | |
self.setPageTitle(); | |
} | |
}; | |
ProgressBar.prototype.runTimer = function () { | |
var self = this; | |
if (!self._timer && self._player.playing) { | |
self._timer = setInterval(function () { | |
if (!self._isScrubbing) { | |
self.updatePosition(); | |
} | |
}.bind(self), self._updateInterval); | |
} else if (self._timer && !self._player.playing) { | |
self.stopTimer(); | |
} | |
}; | |
ProgressBar.prototype.stopTimer = function () { | |
var self = this; | |
clearInterval(self._timer); | |
self._timer = null; | |
}; | |
ProgressBar.prototype.pauseTimer = function (ms) { | |
if (!ms) { | |
throw new Error('You must enter a pause time'); | |
} | |
var self = this; | |
self.stopTimer(); | |
setTimeout(function () { | |
self.runTimer(); | |
}.bind(self), ms); | |
}; | |
ProgressBar.prototype.updateElapsedTime = function (ms) { | |
var newTimeSecs = playerUtils.secsToMins(ms / 1000); | |
this._trackCurrentPos.textContent = newTimeSecs; | |
}; | |
ProgressBar.prototype.enable = function () { | |
if (this._control && !this._adBreak.inProgress()) { | |
this._control.enable(); | |
} | |
}; | |
ProgressBar.prototype.disable = function () { | |
//beej: this._control.disable(); | |
}; | |
ProgressBar.prototype.reload = function () { | |
this._control.handleResize(); | |
this.setupMarkerSizing(); | |
}; | |
ProgressBar.prototype.updatePosition = function () { | |
var self = this; | |
var expectAdBreakUpdates = self._adBreak.inProgress() && self._adBreak.getDetails(); | |
if (!self._player || !self._player.track || !self._player.playing || self._isScrubbing || expectAdBreakUpdates) { | |
self.stopTimer(); | |
return false; | |
} | |
var time = self._player.position; | |
if (time > self._player.track.duration) { | |
time = self._player.track.duration; | |
} | |
if (self._seekingTo && Math.abs(self._seekingTo - time) > 1500) { | |
return false; | |
} | |
self._seekingTo = null; | |
self._control.jumpToStep(time, false); | |
self.updateElapsedTime(time); | |
}; | |
exports.ProgressBar = ProgressBar; | |
}(require('node_modules/api/scripts/models.js'), require('scripts/player-utils.js'), require('scripts/utils.slider.js').Slider)); | |
}, | |
'scripts/player.volumecontrol.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models, pu, Slider) { | |
var playerUtils = pu.playerUtils; | |
var playerStorage = pu.playerStorage; | |
function VolumeControl(wrapperID, logger) { | |
var self = this; | |
var player = models.player; | |
self._wrapper = document.getElementById(wrapperID); | |
self._dragging = false; | |
self._store = new playerStorage(); | |
self.logger = logger; | |
player.load(['volume']).done(function (player) { | |
self._player = player; | |
self.init(); | |
}); | |
} | |
VolumeControl.prototype.reload = function () { | |
this._control.handleResize(); | |
}; | |
VolumeControl.prototype.buildNodes = function () { | |
var self = this; | |
this._barClickArea = document.createElement('div'); | |
this._barClickArea.id = 'volume-click'; | |
this._barOuter = document.createElement('div'); | |
this._barOuter.id = 'volume-bar'; | |
this._handle = document.createElement('span'); | |
this._handle.id = 'vol-position'; | |
this._barInner = document.createElement('div'); | |
this._barInner.id = 'vol-bar-inner'; | |
this._barClickArea.appendChild(this._barOuter); | |
this._barOuter.appendChild(this._barInner); | |
this._barOuter.appendChild(this._handle); | |
this._wrapper.appendChild(this._barClickArea); | |
this._handleWidth = this._handle.offsetWidth; | |
}; | |
VolumeControl.prototype.init = function () { | |
var self = this; | |
var tempVol = self._store.get('spVolume') || 100; | |
tempVol = tempVol < 0 ? 0 : tempVol > 100 ? 100 : tempVol; | |
self.buildNodes(); | |
self._control = new Slider(self._barClickArea, self._handle, { | |
steps: 100, | |
start: parseInt(tempVol, 10), | |
onStart: function () { | |
self._dragging = true; | |
playerUtils.addClass(self._wrapper, 'in-use'); | |
}, | |
onChange: function () { | |
self._player.setVolume(this.step / 100); | |
var innerBarWidth = Math.floor(this.step / this._opts.steps * this.totalPx + self._handleWidth); | |
self._barInner.style.width = innerBarWidth + 'px'; | |
}, | |
onComplete: function () { | |
self._dragging = false; | |
playerUtils.removeClass(self._wrapper, 'in-use'); | |
self._store.set('spVolume', this.step); | |
if (self._player && self._player.track) { | |
self.logger.userHit('volume_bar', { | |
track_id: self._player.track.uri, | |
volume: this.step | |
}); | |
} | |
} | |
}); | |
self._player.addEventListener('change:volume', function () { | |
if (!self._dragging) { | |
self.setVolume(parseInt(self._player.volume * 100, 10)); | |
} | |
}); | |
}; | |
VolumeControl.prototype.setVolume = function (vol) { | |
if (isNaN(vol) || vol < 0 || vol > 100) { | |
throw new Error('Volume must be an integer between 0 and 100'); | |
} | |
this._control.jumpToStep(vol, true); | |
}; | |
exports.VolumeControl = VolumeControl; | |
}(require('node_modules/api/scripts/models.js'), require('scripts/player-utils.js'), require('scripts/utils.slider.js').Slider)); | |
}, | |
'scripts/player.tracking.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (Models) { | |
function Tracker() { | |
this.DEFAULT_DATA = {}; | |
this.DEFAULT_CONTEXT = 'player-web'; | |
this.DEFAULT_EVENT_VERSION = '1'; | |
this.DEFAULT_TEST_VERSION = 'base'; | |
} | |
Tracker.prototype.track = function (event, data, context, eventVersion, testVersion) { | |
data = data || this.DEFAULT_DATA; | |
context = context || this.DEFAULT_CONTEXT; | |
eventVersion = eventVersion || this.DEFAULT_EVENT_VERSION; | |
testVersion = testVersion || this.DEFAULT_TEST_VERSION; | |
Models.application.clientEvent(context, event, eventVersion, testVersion, data); | |
}; | |
var Events = { DOWNLOAD_LINK_CLICKED: 'download-link-clicked' }; | |
exports.Tracker = Tracker; | |
exports.Events = Events; | |
}(require('node_modules/api/scripts/models.js'))); | |
}, | |
'scripts/preview.player.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (playerUtils, models) { | |
'use strict'; | |
var preview = null; | |
var player = null; | |
var previewing = false; | |
var playing = false; | |
var previewUiActive = false; | |
function previewChangeHandler(e) { | |
previewing = e.target.playing; | |
if (previewing === true) { | |
showOverlay(); | |
} else { | |
hideOverlay(); | |
} | |
} | |
function playerChangeHandler(e) { | |
playing = e.target.playing; | |
if (previewing === true && playing === true) { | |
hideOverlay(); | |
} | |
} | |
function showOverlay() { | |
if (!previewUiActive) { | |
playerUtils.addClass(document.body, 'audio-previewing'); | |
previewUiActive = true; | |
} | |
} | |
function hideOverlay() { | |
if (previewUiActive) { | |
playerUtils.removeClass(document.body, 'audio-previewing'); | |
previewUiActive = false; | |
} | |
} | |
function init() { | |
var players = [ | |
models.preview, | |
models.player | |
]; | |
var promises = []; | |
players.forEach(function (p) { | |
promises.push(p.load([ | |
'playing', | |
'track', | |
'context' | |
])); | |
}); | |
models.Promise.join(promises).each(function (p) { | |
if (p.id === 'main') { | |
player = p; | |
player.addEventListener('change:playing', playerChangeHandler); | |
} else if (p.id === 'preview') { | |
preview = p; | |
preview.addEventListener('change:playing', previewChangeHandler); | |
} | |
}); | |
} | |
exports.init = init; | |
}(require('scripts/player-utils.js').playerUtils, require('node_modules/api/scripts/models.js'))); | |
}, | |
'node_modules/api/scripts/hermes.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models) { | |
var Loadable = models.Loadable; | |
var Promise = models.Promise; | |
var ReadyState = { | |
CONNECTING: 0, | |
OPEN: 1, | |
CLOSING: 2, | |
CLOSED: 3 | |
}; | |
function _type(type) { | |
return type.schema.id + '#' + type.name; | |
} | |
function Hermes() { | |
} | |
Hermes.get = function (uri, resultTypes, argumentTypes, timeout) { | |
return new Request('GET', uri, resultTypes, argumentTypes, timeout); | |
}; | |
Hermes.request = function (method, uri, resultTypes, argumentTypes, timeout) { | |
return new Request(method, uri, resultTypes, argumentTypes, timeout); | |
}; | |
Hermes.subscribe = function (uri, resultTypes, argumentTypes, args) { | |
var promise = new models.Promise(); | |
var done = function (data) { | |
promise.setDone(new Subscription(data.subscription_id)); | |
}; | |
var fail = function (_, error) { | |
promise.setFail(error); | |
}; | |
var rload = Promise.join(resultTypes.map(function (type) { | |
return type.schema.load('id'); | |
})); | |
var aload = Promise.join(argumentTypes.map(function (type) { | |
return type.schema.load('id'); | |
})); | |
Promise.join(rload, aload).done(function () { | |
var rtypes = resultTypes.map(_type); | |
var atypes = argumentTypes.map(_type); | |
SP.request('hermes_subscribe', [ | |
uri, | |
rtypes, | |
atypes, | |
args | |
], null, done, fail); | |
}).fail(fail); | |
return promise; | |
}; | |
function Request(method, uri, resultTypes, argumentTypes, timeout) { | |
Loadable.call(this); | |
this.resolve('uri', uri); | |
this.resolve('method', method); | |
this.resolve('timeout', timeout || 0); | |
this._rtypes = resultTypes; | |
this._atypes = argumentTypes; | |
var rload = Promise.join(this._rtypes.map(function (type) { | |
return type.schema.load('id'); | |
})); | |
var aload = Promise.join(this._atypes.map(function (type) { | |
return type.schema.load('id'); | |
})); | |
this._load = Promise.join(rload, aload); | |
} | |
SP.inherit(Request, Loadable); | |
Loadable.define(Request, [ | |
'uri', | |
'method', | |
'timeout' | |
]); | |
Request.prototype.send = function (var_args) { | |
var promise = new Promise(); | |
var request = this; | |
var reqArg = [].slice.call(arguments); | |
this._load.done(function () { | |
var done = function (data) { | |
promise.setDone(data.result); | |
}; | |
var rtypes = request._rtypes.map(_type); | |
var atypes = request._atypes.map(_type); | |
var requestArgs = [ | |
request.uri, | |
request.method, | |
rtypes, | |
atypes, | |
reqArg, | |
request.timeout | |
]; | |
SP.request('hermes_send_request', requestArgs, promise, done, promise.setFail); | |
}).fail(function (o, error) { | |
promise.setFail(error); | |
}); | |
return promise; | |
}; | |
function Schema(urls) { | |
Loadable.call(this); | |
this._urls = urls; | |
} | |
SP.inherit(Schema, Loadable); | |
Loadable.define(Schema, ['id'], '_register'); | |
Schema.fromURL = function (urls) { | |
if (typeof urls === 'string') | |
urls = [urls]; | |
return new this(urls); | |
}; | |
Schema.prototype.type = function (name) { | |
return { | |
schema: this, | |
name: name | |
}; | |
}; | |
Schema.prototype._register = function (propsMask) { | |
var load = function (data) { | |
this.resolveMany(propsMask, data); | |
}; | |
var fail = function (oops) { | |
this.resolveFail(propsMask, oops); | |
}; | |
SP.request('hermes_register_schema', this._urls, this, load, fail); | |
}; | |
function Subscription(id) { | |
models.BridgeLoadable.call(this); | |
this.readyState = ReadyState.OPEN; | |
this._id = id; | |
} | |
SP.inherit(Subscription, models.BridgeLoadable); | |
Subscription.prototype._observed = function () { | |
if (this.readyState != ReadyState.OPEN) | |
return; | |
this.bridgeListen('hermes_event_wait', [this._id]); | |
this.addEventListener('close', function onClose() { | |
this.removeEventListener('close', onClose); | |
this.bridgeUnlisten(); | |
this.readyState = ReadyState.CLOSED; | |
}); | |
}; | |
Subscription.prototype.close = function () { | |
if (this.readyState != ReadyState.OPEN) | |
return; | |
this.readyState = ReadyState.CLOSING; | |
SP.request('hermes_unsubscribe', [this._id]); | |
}; | |
exports.Hermes = Hermes; | |
exports.ReadyState = ReadyState; | |
exports.Schema = Schema; | |
exports.Subscription = Subscription; | |
}(require('node_modules/api/scripts/models.js'))); | |
}, | |
'scripts/player.suggestions.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (hermes, models) { | |
var schema = hermes.Schema.fromURL(['proto/radio.proto']); | |
schema.load().done(function () { | |
var Hermes = hermes.Hermes; | |
function getTrackSuggestions(seed, length, onSuccess, onFail) { | |
makeHermesCall(seed, length, onSuccess, onFail); | |
} | |
; | |
function makeHermesCall(seed, length, onSuccess, onFail) { | |
var salt = Math.floor(Math.random() * 1000000); | |
var suggestionRequest = { | |
salt: salt, | |
uris: seed, | |
lastTracks: [], | |
length: length | |
}; | |
var req = Hermes.get('hm://radio/', [schema.type('Tracks')], [schema.type('RadioRequest')]); | |
req.send(suggestionRequest).done(onSuccess).fail(onFail); | |
} | |
exports.getTrackSuggestions = getTrackSuggestions; | |
}).fail(function () { | |
}); | |
}(require('node_modules/api/scripts/hermes.js'), require('node_modules/api/scripts/models.js'))); | |
}, | |
'node_modules/revgen-shared/scripts/audioad.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models) { | |
var player = models.player; | |
var track = models.track; | |
function AudioAd() { | |
} | |
AudioAd.prototype._showLightbox = function (args, opt_origin) { | |
var appArguments = ['revgen-modal']; | |
appArguments = appArguments.concat(args); | |
appArguments.push([ | |
opt_origin, | |
Date.now() | |
]); | |
return models.application.openApp.apply(models.application, appArguments); | |
}; | |
AudioAd.prototype.handleAdClick = function (adMetadata, isPlayerTrack) { | |
var isLightbox, adURI, embed, backgroundImage; | |
if (isPlayerTrack === undefined) { | |
isPlayerTrack = true; | |
} | |
if (adMetadata && typeof adMetadata === 'object' && typeof adMetadata.targetUrl === 'string') { | |
backgroundImage = embed.background_image || ''; | |
isLightbox = adMetadata.type === 'lightbox'; | |
adURI = adMetadata.targetUrl || ''; | |
embed = adMetadata.embed || {}; | |
} else { | |
isLightbox = false; | |
adURI = adMetadata || ''; | |
embed = {}; | |
} | |
if (adURI.indexOf('http') !== -1) { | |
if (isLightbox) { | |
this._showLightbox([ | |
adURI, | |
embed.width, | |
embed.height, | |
backgroundImage | |
]); | |
} else { | |
window.open(adURI); | |
} | |
if (isPlayerTrack) { | |
window.parent.postMessage('{"ad_clicked":"' + adURI + '"}', '*'); | |
} | |
return; | |
} | |
var uri = adURI.toSpotifyURI(); | |
if (uri.indexOf('spotify:track') !== -1) { | |
player.playTrack(track.fromURI(uri)); | |
} else { | |
models.application.openURI(uri); | |
} | |
if (isPlayerTrack) { | |
window.parent.postMessage('ad_clicked', '*'); | |
} | |
}; | |
exports.AudioAd = new AudioAd(); | |
}(require('node_modules/api/scripts/models.js'))); | |
}, | |
'node_modules/views/scripts/contextapp.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models) { | |
exports.ContextApp = ContextApp; | |
function ContextApp() { | |
} | |
ContextApp.show = function (name, args, element, opt_origin, opt_index, opt_loggingContext) { | |
var offset = element.getBoundingClientRect(); | |
var appArguments = [ | |
name, | |
offset.left, | |
offset.top, | |
offset.width, | |
offset.height | |
]; | |
appArguments = appArguments.concat(args); | |
appArguments.push(opt_origin); | |
appArguments.push(opt_index); | |
appArguments.push(opt_loggingContext); | |
return models.application.openApp.apply(models.application, appArguments); | |
}; | |
}(require('node_modules/api/scripts/models.js'))); | |
}, | |
'node_modules/logging-utils/scripts/logger.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (app, session) { | |
'use strict'; | |
var CONSOLE_METHOD = { | |
DEBUG: 'debug', | |
LOG: 'log', | |
WARN: 'warn', | |
ERROR: 'error' | |
}; | |
var loggers = {}; | |
function Logger(tag, context) { | |
this.tag = tag; | |
this.context = context; | |
this.timerData = {}; | |
this.timerOptions = {}; | |
this.inRolloutPercentage = true; | |
this.setLogOutputLevel(); | |
} | |
Logger.forTag = function (tag, context) { | |
if (typeof tag != 'string' || tag.length < 1) { | |
throw 'Improper tag name.'; | |
} | |
var returnLogger = loggers[tag]; | |
if (!returnLogger) { | |
returnLogger = new Logger(tag, context); | |
loggers[tag] = returnLogger; | |
} | |
return returnLogger; | |
}; | |
Logger.OUTPUT_LEVEL = { | |
DEBUG: 4, | |
LOG: 3, | |
INFO: 2, | |
ERROR: 1, | |
NONE: 0 | |
}; | |
Logger.prototype.setLogOutputLevel = function (logOutputLevel) { | |
var logOutputGobalOverride = null, debuggingEnabled = false; | |
if (logOutputGobalOverride) { | |
this.logOutputLevel = logOutputGobalOverride; | |
} else if (logOutputLevel === undefined || logOutputLevel < Logger.OUTPUT_LEVEL.NONE || logOutputLevel > Logger.OUTPUT_LEVEL.DEBUG) { | |
var suffix = 'spotify.net'; | |
var hostname = window.location.hostname; | |
try { | |
debuggingEnabled = window.localStorage && !!localStorage.getItem('logging_debug'); | |
} catch (e) { | |
} | |
if (debuggingEnabled) { | |
this.logOutputLevel = Logger.OUTPUT_LEVEL.DEBUG; | |
} else if (hostname.indexOf(suffix, hostname.length - suffix.length) !== -1) { | |
this.logOutputLevel = Logger.OUTPUT_LEVEL.ERROR; | |
} else { | |
this.logOutputLevel = Logger.OUTPUT_LEVEL.NONE; | |
} | |
} else { | |
this.logOutputLevel = logOutputLevel; | |
} | |
}; | |
Logger._testVersion = 'base'; | |
Logger.setTestVersion = function (testVersion) { | |
if (typeof testVersion != 'string' || testVersion.length < 1) { | |
throw 'Improper test name.'; | |
} | |
Logger._testVersion = testVersion; | |
}; | |
Logger.prototype.setTestRollout = function (percentage) { | |
var self = this; | |
this.inRolloutPercentage = false; | |
if (percentage > 0) { | |
session.load('testGroup').done(function (s) { | |
var tg = parseInt(s.testGroup, 10); | |
if (tg <= percentage * 10) { | |
self.inRolloutPercentage = true; | |
} | |
}); | |
} | |
}; | |
var ALLOWED_EVENTS = { | |
USER_HOLD_TIMER: 'user:hold', | |
USER_HIT: 'user:hit', | |
USER_SELECT: 'user:select', | |
USER_HOVER: 'user:hover', | |
USER_IMPRESSION: 'user:impression', | |
INFO_TIMER_DEFAULT: 'info:timer', | |
INFO_STATE_LOAD_TIMER: 'info:state_load_timer', | |
INFO_DEFAULT: 'info:default', | |
INFO_WARN: 'info:warn', | |
ERROR_DEFAULT: 'error:user_action_fail', | |
ERROR_USER_ACTION_FAIL: 'error:user_action_fail', | |
ERROR_RENDER_FAIL: 'error:render_fail', | |
DEBUG: 'debug' | |
}; | |
var C = CONSOLE_METHOD, O = Logger.OUTPUT_LEVEL, E = ALLOWED_EVENTS; | |
Logger.prototype.debug = function (eventVersion, data, opt_context) { | |
return this._log(C.DEBUG, E.DEBUG, O.DEBUG, eventVersion, data, opt_context, true, true); | |
}; | |
Logger.prototype.log = function (eventVersion, data, opt_context) { | |
return this._log(C.LOG, E.DEBUG, O.LOG, eventVersion, data, opt_context, true); | |
}; | |
Logger.prototype.userHit = function (eventVersion, data, opt_context) { | |
return this._log(C.LOG, E.USER_HIT, O.INFO, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.userSelect = function (eventVersion, data, opt_context) { | |
return this._log(C.LOG, E.USER_SELECT, O.INFO, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.userHover = function (eventVersion, data, opt_context) { | |
return this._log(C.LOG, E.USER_HOVER, O.INFO, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.userImpression = function (eventVersion, data, opt_context) { | |
return this._log(C.LOG, E.USER_IMPRESSION, O.INFO, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.info = function (eventVersion, data, opt_context) { | |
return this._log(C.LOG, E.INFO_DEFAULT, O.INFO, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.infoWarn = function (eventVersion, data, opt_context) { | |
return this._log(C.WARN, E.INFO_WARN, O.INFO, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.error = function (eventVersion, data, opt_context) { | |
return this._log(C.ERROR, E.ERROR_DEFAULT, O.ERROR, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.errorUserActionFail = function (eventVersion, data, opt_context) { | |
return this._log(C.ERROR, E.ERROR_USER_ACTION_FAIL, O.ERROR, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.errorRenderFail = function (eventVersion, data, opt_context) { | |
return this._log(C.ERROR, E.ERROR_RENDER_FAIL, O.ERROR, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.startHoldTimer = function (eventVersion, data, opt_context) { | |
this._startTimer(eventVersion, data, E.USER_HOLD_TIMER, opt_context); | |
}; | |
Logger.prototype.startTimer = function (eventVersion, data, opt_context) { | |
this._startTimer(eventVersion, data, E.INFO_TIMER_DEFAULT, opt_context); | |
}; | |
Logger.prototype.startStateLoadTimer = function (eventVersion, data, opt_context) { | |
this._startTimer(eventVersion, data, E.INFO_STATE_LOAD_TIMER, opt_context); | |
}; | |
Logger.prototype.startDebugTimer = function (eventVersion, data, opt_context) { | |
this._startTimer(eventVersion, data, E.DEBUG_TIMER, opt_context); | |
}; | |
Logger.prototype.hasTimer = function (eventVersion) { | |
return this.timerData[eventVersion] ? true : false; | |
}; | |
Logger.prototype.overrideStartTime = function (eventVersion, newStartTime) { | |
this.timerData[eventVersion].timerStart = newStartTime; | |
}; | |
Logger.prototype.cancelTimer = function (eventVersion) { | |
delete this.timerData[eventVersion]; | |
delete this.timerOptions[eventVersion]; | |
}; | |
Logger.prototype.tickTimer = function (eventVersion, tickName) { | |
if (!this.timerData[eventVersion]) { | |
return this.error('cannot_tick_unstarted_timer', { timerName: eventVersion }); | |
} | |
tickName = 'timer_tick_' + tickName; | |
var data = this.timerData[eventVersion]; | |
var tickDiff = new Date().getTime() - data.timerStart; | |
data[tickName] = tickDiff; | |
return true; | |
}; | |
Logger.prototype.endTimer = function (eventVersion, opt_overrideTotalTime) { | |
if (!this.timerData[eventVersion]) { | |
return this.error('cannot_end_unstarted_timer', { timerName: eventVersion }); | |
} | |
var data = this.timerData[eventVersion]; | |
if (typeof opt_overrideTotalTime == 'number') { | |
data['timer_total_time'] = opt_overrideTotalTime; | |
} else { | |
var timerEnd = new Date().getTime(); | |
data['timer_total_time'] = timerEnd - data.timerStart; | |
} | |
delete data.timerStart; | |
var opts = this.timerOptions[eventVersion]; | |
var isDebug = opts.timerEvent == ALLOWED_EVENTS.DEBUG_TIMER; | |
var retVal = this._log(isDebug ? CONSOLE_METHOD.DEBUG : CONSOLE_METHOD.LOG, opts.timerEvent, isDebug ? Logger.OUTPUT_LEVEL.DEBUG : Logger.OUTPUT_LEVEL.INFO, eventVersion, data, opts.context, isDebug); | |
this.cancelTimer(eventVersion); | |
return retVal; | |
}; | |
Logger.prototype.addDataToTimer = function (eventVersion, propName, propValue) { | |
if (!this.timerData[eventVersion]) { | |
return this.error('cannot_add_data_to_unstarted_timer', { timerName: eventVersion }); | |
} | |
var tickNamePattern = /^timer_tick_\d*/; | |
if (propName == 'timerStart' || tickNamePattern.test(propName)) { | |
return this.error('cant_override_tick_info', { timerName: eventVersion }); | |
} else { | |
this.timerData[eventVersion][propName] = propValue; | |
} | |
}; | |
Logger.prototype._startTimer = function (eventVersion, data, timerEvent, opt_context) { | |
if (this.timerData[eventVersion]) { | |
return this.error('cannot_restart_timer', { timerName: eventVersion }); | |
} | |
data = data ? data : {}; | |
data.timerStart = new Date().getTime(); | |
this.timerData[eventVersion] = data; | |
this.timerOptions[eventVersion] = { | |
timerEvent: timerEvent, | |
context: opt_context | |
}; | |
return true; | |
}; | |
Logger.prototype._getErrorObject = function () { | |
try { | |
throw new Error(''); | |
} catch (err) { | |
return err; | |
} | |
}; | |
Logger.prototype._getBackendData = function (data) { | |
if (!data) { | |
return {}; | |
} | |
Object.keys(data).forEach(function (key) { | |
var val = data[key]; | |
data[key] = typeof val === 'undefined' ? '' : val; | |
}); | |
if (typeof data == 'string' || typeof data == 'number' || typeof data == 'boolean') { | |
return { data: data }; | |
} | |
if (typeof data != 'object') { | |
return { error: 'unparsable_data' }; | |
} | |
var backendData = {}; | |
for (var key in data) { | |
if (data.hasOwnProperty(key) && (typeof data[key] == 'string' || typeof data[key] == 'number' || typeof data[key] == 'boolean')) { | |
backendData[key] = data[key]; | |
} | |
} | |
return backendData; | |
}; | |
Logger.prototype._log = function (consoleMethod, event, minOutputLevel, eventVersion, data, opt_context, opt_debugOnly, opt_printStacktrace) { | |
var sole = typeof console !== 'undefined'; | |
var context; | |
if (opt_context) | |
context = opt_context; | |
else | |
context = this.context ? this.context : ''; | |
event = event ? event : ''; | |
eventVersion = eventVersion ? eventVersion : ''; | |
if (!opt_debugOnly && this.inRolloutPercentage) { | |
var backendData = this._getBackendData(data); | |
var promise = app.clientEvent(context, event, eventVersion, Logger._testVersion, backendData); | |
promise.fail(function () { | |
sole && console.error('could_not_log_to_backend'); | |
}); | |
} | |
if (!sole || this.logOutputLevel < minOutputLevel) { | |
return false; | |
} | |
var stackTrace = ''; | |
if (opt_printStacktrace) { | |
var err = this._getErrorObject(); | |
stackTrace = '{no stack-trace available}'; | |
if (err.stack) { | |
stackTrace = err.stack.split('\n').slice(1); | |
} | |
} | |
var consoleFunction = console[consoleMethod] ? console[consoleMethod] : console.log; | |
if (typeof consoleFunction == 'object') { | |
consoleFunction = Function.prototype.bind.call(consoleFunction, console); | |
} | |
data = data ? data : ''; | |
consoleFunction.apply(console, [ | |
'[' + this.tag + ']', | |
context, | |
event, | |
eventVersion, | |
data, | |
stackTrace | |
]); | |
return true; | |
}; | |
exports.Logger = Logger; | |
}(require('node_modules/api/scripts/models.js').application, require('node_modules/api/scripts/models.js').session)); | |
}, | |
'node_modules/revgen-shared/scripts/ad_break.js': function (require, module, exports, global, __filename, __dirname) { | |
var models = require('node_modules/api/scripts/models.js'); | |
var Logger = require('node_modules/logging-utils/scripts/logger.js').Logger; | |
var L = Logger.forTag('RevGen:AdBreak'); | |
function AdBreak() { | |
L.log('construct'); | |
models.Observable.call(this); | |
this._isActive = true; | |
this._adBreakInProgress = false; | |
this._details = null; | |
} | |
SP.inherit(AdBreak, models.Observable); | |
AdBreak.prototype.init = function (player) { | |
this._player = player; | |
if (!player) { | |
throw new Error('AdBreak.init: models~Player is a required parameter'); | |
} | |
this._listen(); | |
}; | |
AdBreak.prototype.destroy = function () { | |
if (this._isActive) { | |
this._isActive = false; | |
} | |
}; | |
AdBreak.prototype.inProgress = function () { | |
if (this._player && this._player.track && this._player.track.advertisement) { | |
return true; | |
} | |
return this._adBreakInProgress; | |
}; | |
AdBreak.prototype.resume = function () { | |
if (this._details) { | |
L.log('request ads_resume'); | |
SP.request('ads_resume', []); | |
} else { | |
if (this._player.volume === 0) { | |
this._player.setVolume(0.1); | |
} | |
this._player.play(); | |
} | |
}; | |
AdBreak.prototype.pause = function () { | |
if (this._details) { | |
L.log('request ads_pause'); | |
SP.request('ads_pause', []); | |
} else { | |
this._player.pause(); | |
} | |
}; | |
AdBreak.prototype.getDetails = function () { | |
var details = null; | |
if (this._details) { | |
details = {}; | |
for (var k in this._details) { | |
if (this._details.hasOwnProperty(k)) { | |
details[k] = this._details[k]; | |
} | |
} | |
} | |
return details; | |
}; | |
AdBreak.prototype._listen = function () { | |
var _this = this; | |
SP.request('ads_event_wait', [], this, function (event) { | |
var dispatch = false; | |
L.log('event received', event.type, event); | |
if (_this._isActive) { | |
_this._listen(); | |
switch (event.type) { | |
case 'ads_break_started': | |
_this._adBreakInProgress = true; | |
_this.dispatchEvent('startBreak'); | |
break; | |
case 'ads_break_ended': | |
_this._adBreakInProgress = false; | |
_this._details = null; | |
_this.dispatchEvent('endBreak'); | |
break; | |
case 'ads_break_change': | |
var received = _this._details === null || _this._details.imageUrl != event.data.params.imageUrl; | |
_this._details = event.data.params; | |
if (received) { | |
_this.dispatchEvent('receiveDetails'); | |
} | |
_this.dispatchEvent('updateDetails'); | |
break; | |
} | |
if (dispatch) { | |
} | |
} | |
}, function (err) { | |
L.error('error listening', err); | |
}); | |
}; | |
module.exports = AdBreak; | |
}, | |
'node_modules/api/scripts/toplists.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models) { | |
var Loadable = models.Loadable; | |
var BridgeCollection = models.BridgeCollection; | |
var Album = models.Album; | |
var Artist = models.Artist; | |
var Playlist = models.Playlist; | |
var Track = models.Track; | |
var session = models.session; | |
function Toplist(request, prefix, suffix) { | |
Loadable.call(this); | |
this.resolve('uri', prefix + 'tracks' + suffix); | |
this.resolve('albums', new BridgeCollection(Album, prefix + 'albums' + suffix, request + '_albums')); | |
this.resolve('artists', new BridgeCollection(Artist, prefix + 'artists' + suffix, request + '_artists')); | |
this.resolve('tracks', new BridgeCollection(Track, prefix + 'tracks' + suffix, request + '_tracks')); | |
this.resolve('playlists', new BridgeCollection(Playlist, prefix + 'playlists' + suffix, request + '_playlists')); | |
} | |
SP.inherit(Toplist, Loadable); | |
Loadable.define(Toplist, [ | |
'albums', | |
'artists', | |
'tracks', | |
'playlists', | |
'uri' | |
]); | |
Toplist.forCurrentUser = function () { | |
var prefix = session.user.uri + ':top:'; | |
var suffix = ''; | |
return new this('toplist_user', prefix, suffix); | |
}; | |
Toplist.forUser = function (user) { | |
var prefix = user.uri + ':top:'; | |
var suffix = ''; | |
return new this('toplist_user', prefix, suffix); | |
}; | |
Toplist.forWorld = function () { | |
var prefix = 'spotify:top:'; | |
var suffix = ':global'; | |
return new this('toplist_region', prefix, suffix); | |
}; | |
Toplist.forCurrentRegion = function () { | |
var prefix = 'spotify:top:'; | |
var suffix = ':country:USER'; | |
return new this('toplist_region', prefix, suffix); | |
}; | |
Toplist.forRegion = function (region) { | |
var prefix = 'spotify:top:'; | |
var suffix = ':country:' + region; | |
return new this('toplist_region', prefix, suffix); | |
}; | |
Toplist.forArtist = function (artist, region) { | |
var prefix = artist.uri + ':top:'; | |
var suffix = region ? ':country:' + region : ''; | |
return new this('toplist_artist', prefix, suffix); | |
}; | |
exports.Toplist = Toplist; | |
}(require('node_modules/api/scripts/models.js'))); | |
}, | |
'node_modules/views/scripts/utils/dom.js': function (require, module, exports, global, __filename, __dirname) { | |
var slice = Array.prototype.slice; | |
exports.id = function (id) { | |
return document.getElementById(id); | |
}; | |
exports.query = function (selector, context) { | |
context = context || document; | |
return context.querySelector(selector); | |
}; | |
exports.queryAll = function (selector, context) { | |
context = context || document; | |
return slice.call(context.querySelectorAll(selector)); | |
}; | |
exports.queryClasses = function (className, context) { | |
context = context || document; | |
return slice.call(context.getElementsByClassName(className)); | |
}; | |
exports.queryTags = function (tag, context) { | |
context = context || document; | |
return slice.call(context.getElementsByTagName(className)); | |
}; | |
exports.addEventListener = function (elem, event, handler, useCapture) { | |
if (elem.addEventListener) { | |
elem.addEventListener(event, handler, !!useCapture); | |
} else if (elem.attachEvent) { | |
var wrapperHandler = function (e) { | |
handler.call(elem, e); | |
}; | |
handler.wrapperHandler = wrapperHandler; | |
elem.attachEvent('on' + event, wrapperHandler); | |
} | |
}; | |
exports.removeEventListener = function (elem, event, handler, useCapture) { | |
if (elem.removeEventListener) { | |
elem.removeEventListener(event, handler, !!useCapture); | |
} else if (elem.detachEvent) { | |
elem.detachEvent('on' + event, handler.wrapperHandler || handler); | |
} | |
}; | |
}, | |
'node_modules/views/scripts/utils/dnd.js': function (require, module, exports, global, __filename, __dirname) { | |
function Drag() { | |
this.customElementHandlers = []; | |
this.hasDragSupport = !!SP.addDragHandler; | |
if (this.hasDragSupport) { | |
var testCustom = SP.bind(this._testCustom, this); | |
var getCustomText = SP.bind(this._getCustomText, this); | |
var getCustomData = SP.bind(this._getCustomData, this); | |
this.addHandler(testCustom, getCustomText, getCustomData); | |
} | |
} | |
Drag.prototype.addHandler = function (testFunc, getDataFunc, getTextFunc) { | |
if (this.hasDragSupport) { | |
SP.addDragHandler(testFunc, getDataFunc, getTextFunc); | |
} | |
}; | |
Drag.prototype.removeHandler = function (testFunc, getDataFunc, getTextFunc) { | |
if (this.hasDragSupport) { | |
SP.removeDragHandler(testFunc, getDataFunc, getTextFunc); | |
} | |
}; | |
Drag.prototype.enableForElement = function (elem, opt_getData, opt_getText) { | |
var getData = opt_getData || function () { | |
return { 'text/plain': elem.title || elem.textContent || '' }; | |
}; | |
var getText = opt_getText || function () { | |
return elem.title || elem.textContent || ''; | |
}; | |
var index = this.customElementHandlers.push({ | |
getText: getText, | |
getData: getData | |
}); | |
elem.setAttribute('data-dnd-custom-index', index.toString()); | |
elem.setAttribute('draggable', 'true'); | |
}; | |
Drag.prototype._getCustomIndex = function (elem) { | |
var dndCustomIndex = elem.getAttribute('data-dnd-custom-index'); | |
return dndCustomIndex !== null && dndCustomIndex !== '' ? dndCustomIndex : false; | |
}; | |
Drag.prototype._testCustom = function (elem) { | |
var dndCustomIndex = this._getCustomIndex(elem); | |
if (dndCustomIndex !== false) { | |
if (this.customElementHandlers[dndCustomIndex]) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
Drag.prototype._getCustomText = function (elem) { | |
var dndCustomIndex = this._getCustomIndex(elem); | |
return this.customElementHandlers[dndCustomIndex].getText(); | |
}; | |
Drag.prototype._getCustomData = function (elem) { | |
var dndCustomIndex = this._getCustomIndex(elem); | |
return this.customElementHandlers[dndCustomIndex].getData(); | |
}; | |
exports.drag = new Drag(); | |
}, | |
'node_modules/views/supported-languages.json': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = [ | |
'arb', | |
'bn', | |
'de', | |
'en', | |
'fi', | |
'fr', | |
'el', | |
'es', | |
'es-la', | |
'es-419', | |
'hi', | |
'hu', | |
'id', | |
'ja', | |
'ko', | |
'nl', | |
'pl', | |
'pt-br', | |
'it', | |
'ro', | |
'ru', | |
'ta', | |
'th', | |
'tr', | |
'zh-hant', | |
'zsm' | |
]; | |
}, | |
'node_modules/views/scripts/utils/css.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models) { | |
var css = exports; | |
var head = document.head || document.getElementsByTagName('head')[0]; | |
var importCache = {}; | |
css.importSheet = function (path) { | |
var resolved = models.application.resolvePath(path); | |
resolved = resolved.replace('scripts/', ''); | |
if (!importCache[resolved]) { | |
importCache[resolved] = true; | |
var link = document.createElement('link'); | |
link.rel = 'stylesheet'; | |
link.href = resolved; | |
head.appendChild(link); | |
return true; | |
} | |
return false; | |
}; | |
css.importSheets = function () { | |
for (var i = 0, l = arguments.length; i < l; i++) { | |
this.importSheet(arguments[i]); | |
} | |
return this; | |
}; | |
var insertedStyles = {}; | |
css.appendStyles = function (id, selectors) { | |
id = 'sp-' + id; | |
if (insertedStyles[id]) | |
return getElementById(id); | |
insertedStyles[id] = 1; | |
var style = document.createElement('style'); | |
style.id = style.name = id; | |
if (!selectors) | |
return style; | |
var styleStr = '', rules, key; | |
for (key in selectors) { | |
if (!selectors.hasOwnProperty(key)) | |
continue; | |
var selector = selectors[key]; | |
styleStr += key; | |
rules = []; | |
for (key in selector) { | |
if (!selector.hasOwnProperty(key)) | |
continue; | |
rules.push(key + ': ' + selector[key]); | |
} | |
styleStr += ' {' + rules.join('; ') + '}\n'; | |
} | |
style.innerHTML = styleStr; | |
head.appendChild(style); | |
return style; | |
}; | |
css.getStyle = 'currentStyle' in head ? function (el, style) { | |
return el.currentStyle[style]; | |
} : function (el, style) { | |
var defaultView = el.ownerDocument.defaultView; | |
if (!defaultView) | |
return null; | |
var computed = defaultView.getComputedStyle(el, null); | |
return !computed ? null : computed.getPropertyValue(style); | |
}; | |
function _trim(string) { | |
return string.replace(/^\s+|\s+$/g, ''); | |
} | |
exports.addClass = function (elements, className) { | |
if (elements.nodeType === 1) { | |
elements = [elements]; | |
} | |
for (var i = 0; i < elements.length; i++) { | |
var element = elements[i]; | |
if (element.nodeType === 1 && (' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) { | |
element.className = _trim(element.className + ' ' + className); | |
} | |
} | |
}; | |
exports.removeClass = function (elements, className) { | |
if (elements.nodeType === 1) { | |
elements = [elements]; | |
} | |
for (var i = 0; i < elements.length; i++) { | |
var element = elements[i]; | |
if (element.nodeType === 1 && (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1) { | |
element.className = _trim(element.className.replace(new RegExp('(\\s|^)' + className + '(\\s|$)', 'gi'), ' ')); | |
} | |
} | |
}; | |
exports.hasClass = function (element, className) { | |
if (!element) | |
return false; | |
return !!~(' ' + element.className + ' ').indexOf(' ' + className + ' '); | |
}; | |
css.classList = { | |
add: function (elem, className) { | |
if (elem && !this.contains(elem, className)) { | |
elem.className = this.trim(elem.className + ' ' + className); | |
} | |
}, | |
remove: function (elem, className) { | |
if (elem) { | |
elem.className = this.trim(elem.className.replace(new RegExp('(\\s|^)' + className + '(\\s|$)', 'gi'), ' ')); | |
} | |
}, | |
contains: function (elem, className) { | |
return elem ? !!~(' ' + elem.className + ' ').indexOf(' ' + className + ' ') : false; | |
}, | |
trim: function (string) { | |
return string.replace(/^\s+|\s+$/g, ''); | |
} | |
}; | |
}(require('node_modules/api/scripts/models.js'))); | |
}, | |
'node_modules/views/scripts/utils/device.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (css) { | |
var ua = navigator.userAgent; | |
var touch = function () { | |
return 'ontouchstart' in window; | |
}(); | |
var mobile = function () { | |
var match = ua.match(/iPhone|iPod|iPad|Android/i); | |
return match ? match[0].toLowerCase() : false; | |
}(); | |
var browser = function () { | |
var match = ua.match(/ie|firefox|chrome|safari/i); | |
return match ? match[0].toLowerCase() : 'other'; | |
}(); | |
var container = function () { | |
var head = document.getElementsByTagName('head')[0]; | |
var scripts = head.getElementsByTagName('script'); | |
for (var i = 0; i < scripts.length; i++) { | |
var src = scripts[i].src; | |
var match = src.match(/\/core\.(\w+)\.js/); | |
if (match) { | |
var result = match[1]; | |
return result === 'browser' ? 'web' : result; | |
} | |
} | |
return 'web'; | |
}(); | |
css.addClass(document.documentElement, 'sp-for-' + (touch && mobile ? 'touch' : 'mouse')); | |
css.addClass(document.documentElement, 'sp-device-' + container); | |
exports.container = container; | |
exports.browser = browser; | |
exports.mobile = mobile; | |
exports.touch = touch; | |
}(require('node_modules/views/scripts/utils/css.js'))); | |
}, | |
'node_modules/views/scripts/utils/touch.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (device, css, models) { | |
var CustomEvent = window.CustomEvent || function (type, eventInitDict) { | |
eventInitDict = eventInitDict || {}; | |
var event = document.createEvent('CustomEvent'); | |
if (event.initCustomEvent) { | |
event.initCustomEvent(type, !!eventInitDict.bubbles, !!eventInitDict.cancelable, eventInitDict.detail); | |
} else { | |
event.initEvent(type, !!eventInitDict.bubbles, !!eventInitDict.cancelable); | |
event.detail = eventInitDict.detail; | |
} | |
return event; | |
}; | |
var hasTouch = device.mobile && device.touch; | |
if (hasTouch) { | |
css.importSheet('$views/css/touch.css'); | |
} | |
var selectionCSSClass = 'sp-ios-selected'; | |
var hasMovedRight = false; | |
var isTouchPending = false; | |
var timer = 0; | |
var touches = { length: 0 }; | |
var isScrolling = false; | |
var isDown = false; | |
var firstID = ''; | |
var firstEvent = null; | |
var numHandlers = 0; | |
var tapSelectionDuration = 50; | |
var eventHandlerData = { | |
'select': [], | |
'deselect': [], | |
'tap': [] | |
}; | |
function Selection() { | |
var self = this; | |
this._onScroll = function (event) { | |
isScrolling = false; | |
}; | |
this._onTouchStart = function (event) { | |
self._onTouchEvent(event, '_handleTouchStart'); | |
}; | |
this._onTouchMove = function (event) { | |
self._onTouchEvent(event, '_handleTouchMove'); | |
}; | |
this._onTouchEnd = function (event) { | |
self._onTouchEvent(event, '_handleTouchEnd'); | |
}; | |
} | |
Selection.prototype.on = function (eventType, element, handler) { | |
var handlerData = eventHandlerData[eventType]; | |
if (!handlerData) | |
return; | |
for (var i = handlerData.length; i--;) { | |
var data = handlerData[i]; | |
if (data.element === element && data.handler === handler) { | |
return; | |
} | |
} | |
handlerData.push({ | |
element: element, | |
handler: handler | |
}); | |
numHandlers++; | |
if (numHandlers === 1) { | |
this._attachHandlers(); | |
} | |
}; | |
Selection.prototype.off = function (eventType, element, opt_handler) { | |
var handlerData = eventHandlerData[eventType]; | |
if (!handlerData) | |
return; | |
for (var i = handlerData.length; i--;) { | |
var data = handlerData[i]; | |
if (data.element === element) { | |
if (!opt_handler || opt_handler && opt_handler === data.handler) { | |
data.splice(i, 1); | |
numHandlers--; | |
if (opt_handler) | |
break; | |
} | |
} | |
} | |
if (numHandlers === 0) { | |
this._detachHandlers(); | |
} | |
}; | |
Selection.prototype.emit = function (eventType, element, event, opt_touch) { | |
SP.defer(this, function () { | |
this.emitSync(eventType, element, event, opt_touch); | |
}); | |
}; | |
Selection.prototype.emitSync = function (eventType, element, event, opt_touch) { | |
var handlerData = eventHandlerData[eventType]; | |
if (!handlerData) | |
return; | |
for (var i = 0, l = handlerData.length; i < l; i++) { | |
var data = handlerData[i]; | |
if (data.element === element) { | |
data.handler.call(this, event, opt_touch); | |
} | |
} | |
}; | |
Selection.prototype.enableForElement = function (element, opt_options) { | |
var options = opt_options || {}; | |
var hasTapped = false; | |
var delay = options.willOpenNewView ? 1000 : undefined; | |
delay = options.removeDelay != null ? options.removeDelay : delay; | |
this.on('select', element, function () { | |
hasTapped = false; | |
selectElement(element, options); | |
}); | |
this.on('deselect', element, function () { | |
setTimeout(function () { | |
deselectElement(element, options); | |
}, hasTapped ? delay : 0); | |
}); | |
this.on('tap', element, function (event) { | |
hasTapped = true; | |
dispatchEvent(element, 'sp-tap', { touchEndObject: event }); | |
}); | |
}; | |
Selection.prototype.disableForElement = function (element) { | |
this.off('select', element); | |
this.off('deselect', element); | |
this.off('tap', element); | |
}; | |
Selection.prototype._attachHandlers = function () { | |
window.addEventListener('scroll', this._onScroll, false); | |
document.addEventListener('touchstart', this._onTouchStart, false); | |
document.addEventListener('touchmove', this._onTouchMove, false); | |
document.addEventListener('touchend', this._onTouchEnd, false); | |
}; | |
Selection.prototype._detachHandlers = function () { | |
window.removeEventListener('scroll', this._onScroll, false); | |
document.removeEventListener('touchstart', this._onTouchStart, false); | |
document.removeEventListener('touchmove', this._onTouchMove, false); | |
document.removeEventListener('touchend', this._onTouchEnd, false); | |
}; | |
Selection.prototype._select = function (event, touch) { | |
var element = findTargetElement(event.target, 'select'); | |
if (!element) | |
return; | |
this.emit('select', element, event, touch); | |
}; | |
Selection.prototype._deselect = function (event, touch) { | |
var element = findTargetElement(event.target, 'deselect'); | |
if (!element) | |
return; | |
this.emit('deselect', element, event, touch); | |
}; | |
Selection.prototype._tap = function (event, touch) { | |
var element = findTargetElement(event.target, 'tap'); | |
if (!element) | |
return; | |
this.emit('tap', element, event, touch); | |
}; | |
Selection.prototype._onTouchEvent = function (event, handlerName) { | |
var changedTouches = event.changedTouches; | |
for (var i = 0, l = changedTouches.length; i < l; i++) { | |
this[handlerName](event, changedTouches[i]); | |
} | |
}; | |
Selection.prototype._handleTouchStart = function (event, touch) { | |
var self = this; | |
touches[touch.identifier] = { | |
identifier: touch.identifier, | |
pageX: touch.pageX, | |
pageY: touch.pageY | |
}; | |
touches.length++; | |
if (touches.length > 1) { | |
return; | |
} | |
if (touches.length === 1) { | |
firstID = touch.identifier; | |
firstEvent = event; | |
} | |
hasMovedRight = false; | |
clearTimeout(timer); | |
if (!isScrolling) { | |
isTouchPending = true; | |
timer = setTimeout(function () { | |
isTouchPending = false; | |
if (!isScrolling && !isDown) { | |
isDown = true; | |
self._select(event, touch); | |
} | |
}, 100); | |
} | |
}; | |
Selection.prototype._handleTouchMove = function (event, touch) { | |
if (!isTouchPending && !isDown) | |
return; | |
if (isTouchPending && !isDown && touch.identifier !== firstID) | |
return; | |
var startTouch = touches[touch.identifier]; | |
if (!startTouch) | |
return; | |
if (!hasMovedRight) { | |
hasMovedRight = touch.pageX > startTouch.pageX; | |
} | |
var isWithinX = !hasMovedRight; | |
var isWithinY = Math.abs(touch.pageY - startTouch.pageY) <= 7; | |
if (!isWithinX || !isWithinY) { | |
if (!isWithinY && !hasMovedRight) { | |
isScrolling = true; | |
} | |
isTouchPending = false; | |
if (isDown) { | |
isDown = false; | |
this._deselect(firstEvent, touch); | |
} | |
} | |
}; | |
Selection.prototype._handleTouchEnd = function (event, touch) { | |
var id = touch.identifier; | |
if (id in touches) { | |
touches.length--; | |
delete touches[id]; | |
} | |
if (id !== firstID) | |
return; | |
var self = this; | |
var removeDelay = isTouchPending ? tapSelectionDuration : 0; | |
clearTimeout(timer); | |
if (isScrolling || !isTouchPending && !isDown) { | |
isDown = false; | |
return; | |
} | |
isDown = false; | |
if (isTouchPending) { | |
isTouchPending = false; | |
this._select(event, touch); | |
} | |
setTimeout(function () { | |
self._tap(event, touch); | |
self._deselect(event, touch); | |
}, removeDelay); | |
}; | |
function findTargetElement(element, eventType) { | |
var handlerData = eventHandlerData[eventType]; | |
for (var i = 0, l = handlerData.length; i < l; i++) { | |
var handlerElement = handlerData[i].element; | |
var elem = element; | |
while (elem) { | |
if (elem === handlerElement) | |
return elem; | |
elem = elem.parentNode; | |
} | |
} | |
return null; | |
} | |
function dispatchEvent(element, eventName, opt_detail) { | |
element.dispatchEvent(new CustomEvent(eventName, { detail: opt_detail || {} })); | |
} | |
function selectElement(element, options) { | |
css.addClass(element, selectionCSSClass); | |
if (options.selectedClassName) { | |
css.addClass(element, options.selectedClassName); | |
} | |
} | |
function deselectElement(element, options) { | |
css.removeClass(element, selectionCSSClass); | |
if (options.selectedClassName) { | |
css.removeClass(element, options.selectedClassName); | |
} | |
} | |
exports.selection = new Selection(); | |
}(require('node_modules/views/scripts/utils/device.js'), require('node_modules/views/scripts/utils/css.js'), require('node_modules/api/scripts/models.js'))); | |
}, | |
'node_modules/mout/lang/kindOf.js': function (require, module, exports, global, __filename, __dirname) { | |
var _rKind = /^\[object (.*)\]$/, _toString = Object.prototype.toString, UNDEF; | |
function kindOf(val) { | |
if (val === null) { | |
return 'Null'; | |
} else if (val === UNDEF) { | |
return 'Undefined'; | |
} else { | |
return _rKind.exec(_toString.call(val))[1]; | |
} | |
} | |
module.exports = kindOf; | |
}, | |
'node_modules/mout/lang/isPlainObject.js': function (require, module, exports, global, __filename, __dirname) { | |
function isPlainObject(value) { | |
return !!value && typeof value === 'object' && value.constructor === Object; | |
} | |
module.exports = isPlainObject; | |
}, | |
'node_modules/mout/array/forEach.js': function (require, module, exports, global, __filename, __dirname) { | |
function forEach(arr, callback, thisObj) { | |
if (arr == null) { | |
return; | |
} | |
var i = -1, len = arr.length; | |
while (++i < len) { | |
if (callback.call(thisObj, arr[i], i, arr) === false) { | |
break; | |
} | |
} | |
} | |
module.exports = forEach; | |
}, | |
'node_modules/mout/array/append.js': function (require, module, exports, global, __filename, __dirname) { | |
function append(arr1, arr2) { | |
if (arr2 == null) { | |
return arr1; | |
} | |
var pad = arr1.length, i = -1, len = arr2.length; | |
while (++i < len) { | |
arr1[pad + i] = arr2[i]; | |
} | |
return arr1; | |
} | |
module.exports = append; | |
}, | |
'node_modules/mout/array/slice.js': function (require, module, exports, global, __filename, __dirname) { | |
function slice(arr, start, end) { | |
var len = arr.length; | |
if (start == null) { | |
start = 0; | |
} else if (start < 0) { | |
start = Math.max(len + start, 0); | |
} else { | |
start = Math.min(start, len); | |
} | |
if (end == null) { | |
end = len; | |
} else if (end < 0) { | |
end = Math.max(len + end, 0); | |
} else { | |
end = Math.min(end, len); | |
} | |
var result = []; | |
while (start < end) { | |
result.push(arr[start++]); | |
} | |
return result; | |
} | |
module.exports = slice; | |
}, | |
'node_modules/mout/function/bind.js': function (require, module, exports, global, __filename, __dirname) { | |
var slice = require('node_modules/mout/array/slice.js'); | |
function bind(fn, context, args) { | |
var argsArr = slice(arguments, 2); | |
return function () { | |
return fn.apply(context, argsArr.concat(slice(arguments))); | |
}; | |
} | |
module.exports = bind; | |
}, | |
'node_modules/api/scripts/library.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models, hermes) { | |
var slice = Array.prototype.slice; | |
function _updateStarredCache(evt) { | |
models.Track._cache.update(evt.uris, { starred: evt.type == 'insert' }); | |
} | |
function Library(uri) { | |
models.BridgeLoadable.call(this); | |
var self = this; | |
var owner = models.User.fromURI(uri); | |
this._initPromise = models.Promise.join([ | |
models.session.load('device'), | |
owner.load('username') | |
]); | |
this._initPromise.done(function () { | |
self._useBridgeForCollection = models.session.device === 'desktop'; | |
}); | |
this.resolve('owner', owner); | |
this.resolve('tracks', new LibraryTracksCollection(this, owner)); | |
this.resolve('albums', new LibraryAlbumsCollection(this, owner)); | |
this.resolve('artists', null); | |
this.resolve('playlists', new models.BridgeCollection(models.Playlist, null, 'library_playlists', owner.uri)); | |
this.resolve('published', new models.BridgeCollection(models.Playlist, null, 'library_published', owner.uri)); | |
this.resolve('starred', models.Playlist.fromURI(owner.uri + ':starred')); | |
this.resolve('toplist', models.Playlist.fromURI(owner.uri + ':toplist')); | |
if (owner.uri == models.session.user.uri) { | |
models.Playlist.getOrCreateLibraryListener().proxyTo(this); | |
this.starred.addEventListener('insert', _updateStarredCache); | |
this.starred.addEventListener('remove', _updateStarredCache); | |
} else { | |
var observedHandler = function () { | |
self.bridgeListen('library_event_wait', [owner.uri]); | |
}; | |
this.playlists._observed = observedHandler; | |
this.published._observed = observedHandler; | |
} | |
} | |
SP.inherit(Library, models.BridgeLoadable); | |
Library.fromURI = models.Cache.lookup; | |
Library._cache = new models.Cache(Library); | |
models.Loadable.define(Library, [ | |
'albums', | |
'artists', | |
'owner', | |
'playlists', | |
'published', | |
'starred', | |
'toplist', | |
'tracks' | |
]); | |
Library.Item; | |
Library.prototype.publish = function (playlists) { | |
return models.promisedRequest(this, 'library_publish', [this.owner.uri].concat(SP.uris(arguments))); | |
}; | |
Library.prototype.star = function (items) { | |
return models.promisedRequest(this, 'library_star', [this.owner.uri].concat(SP.uris(arguments))); | |
}; | |
Library.prototype.subscribe = function (items) { | |
return models.promisedRequest(this, 'library_subscribe', [this.owner.uri].concat(SP.uris(arguments))); | |
}; | |
Library.prototype.unpublish = function (playlists) { | |
return models.promisedRequest(this, 'library_unpublish', [this.owner.uri].concat(SP.uris(arguments))); | |
}; | |
Library.prototype.unstar = function (items) { | |
return models.promisedRequest(this, 'library_unstar', [this.owner.uri].concat(SP.uris(arguments))); | |
}; | |
Library.prototype.unsubscribe = function (items) { | |
return models.promisedRequest(this, 'library_unsubscribe', [this.owner.uri].concat(SP.uris(arguments))); | |
}; | |
Library.prototype.getUnionSources = function (item) { | |
var promise = new models.Promise(); | |
promise.setDone({ | |
collection: true, | |
playlists: [] | |
}); | |
return promise; | |
}; | |
Library.prototype._urisToIds = function (uris) { | |
return uris.map(function (uri) { | |
return uri.substr(-22); | |
}); | |
}; | |
Library.forCurrentUser = function () { | |
return Library.forUser(models.session.user); | |
}; | |
Library.forUser = function (user) { | |
return Library.fromURI(user.uri); | |
}; | |
function AbstractLibraryCollection(library, owner, uri, itemClass, entityName) { | |
this._library = library; | |
this.owner = owner; | |
this.entityName = entityName; | |
this.requestPrefix = 'library_' + entityName; | |
models.BridgeCollection.call(this, itemClass, uri, self.requestPrefix, owner.uri); | |
} | |
SP.inherit(AbstractLibraryCollection, models.BridgeCollection); | |
AbstractLibraryCollection.prototype.makeUri = function (resource) { | |
return 'hm://collection-web/v1/' + encodeURIComponent(this.owner.username) + (resource || '') + '/' + this.entityName; | |
}; | |
AbstractLibraryCollection.prototype.contains = function (items) { | |
var self = this; | |
var uris = SP.uris(arguments); | |
var promise = new models.Promise(); | |
if (!items || items.length == 0) { | |
if (!Array.isArray(items)) | |
promise.setDone(false); | |
else | |
promise.setDone(uris.map(function () { | |
return false; | |
})); | |
return promise; | |
} | |
this._library._initPromise.done(function () { | |
if (self._library._useBridgeForCollection) { | |
var done = function (val) { | |
if (!Array.isArray(items)) | |
promise.setDone(val.in_collection[0]); | |
else | |
promise.setDone(val.in_collection); | |
}; | |
SP.request(self.requestPrefix + '_contains', [self.descriptor].concat(uris), promise, done, promise.setFail); | |
} else { | |
var ids = self._library._urisToIds(uris); | |
hermes.Hermes.request('POST', self.makeUri('/contains'), [], []).send(JSON.stringify(ids)).done(function (result) { | |
var contains = JSON.parse(result[0]); | |
if (!Array.isArray(items)) | |
promise.setDone(contains[0]); | |
else | |
promise.setDone(contains); | |
}).fail(function (_, res) { | |
if (res && res.code && res.code.code === 404) { | |
promise.setDone(false); | |
} else { | |
promise.setFail(res); | |
} | |
}); | |
} | |
}); | |
return promise; | |
}; | |
AbstractLibraryCollection.prototype.add = function (items) { | |
var uris = SP.uris(arguments); | |
var self = this; | |
var promise = new models.Promise(); | |
this._library._initPromise.done(function () { | |
if (self._library._useBridgeForCollection) { | |
SP.request(self.requestPrefix + '_append', [self.descriptor].concat(uris), promise, promise.setDone, promise.setFail); | |
} else { | |
var ids = self._library._urisToIds(uris); | |
hermes.Hermes.request('POST', self.makeUri(), [], []).send(JSON.stringify(ids)).done(function (result) { | |
promise.setDone(); | |
models.client.broadcast('collection-changed'); | |
}).fail(promise.setFail.bind(promise)); | |
} | |
}); | |
return promise; | |
}; | |
AbstractLibraryCollection.prototype.remove = function (ref) { | |
var self = this; | |
var promise = new models.Promise(); | |
this._library._initPromise.done(function () { | |
if (self._library._useBridgeForCollection) { | |
SP.request(self.requestPrefix + '_remove', [ | |
self.descriptor, | |
ref.index, | |
ref.uri | |
], promise, promise.setDone, promise.setFail); | |
} else { | |
var ids = self._library._urisToIds([ref.uri]); | |
hermes.Hermes.request('DELETE', self.makeUri(), [], []).send(JSON.stringify(ids)).done(function (result) { | |
promise.setDone(); | |
models.client.broadcast('collection-changed'); | |
}).fail(promise.setFail.bind(promise)); | |
} | |
}); | |
return promise; | |
}; | |
AbstractLibraryCollection.prototype.clear = function () { | |
throw new Error('Not implemented'); | |
}; | |
AbstractLibraryCollection.prototype.insert = function (ref, items) { | |
throw new Error('Not implemented'); | |
}; | |
AbstractLibraryCollection.prototype.trim = function (ref) { | |
throw new Error('Not implemented'); | |
}; | |
function LibraryTracksCollection(library, owner) { | |
var self = this; | |
AbstractLibraryCollection.call(this, library, owner, owner.uri + ':collection', models.Track, 'tracks'); | |
this._observed = function () { | |
this._library._initPromise.done(function () { | |
if (self._library._useBridgeForCollection) { | |
self.bridgeListen('library_tracks_event_wait', [owner.uri]); | |
} else { | |
models.client.addEventListener('broadcast', function (event) { | |
if (event.message == 'collection-changed') { | |
self.dispatchEvent({ type: 'changed' }); | |
} | |
}); | |
} | |
}); | |
}; | |
} | |
SP.inherit(LibraryTracksCollection, AbstractLibraryCollection); | |
function LibraryAlbumsCollection(library, owner) { | |
var self = this; | |
AbstractLibraryCollection.call(this, library, owner, null, models.Album, 'albums'); | |
this._observed = function () { | |
this._library._initPromise.done(function () { | |
if (self._library._useBridgeForCollection) { | |
self.bridgeListen('library_albums_event_wait', [owner.uri]); | |
} | |
}); | |
}; | |
} | |
SP.inherit(LibraryAlbumsCollection, AbstractLibraryCollection); | |
exports.Library = Library; | |
}(require('node_modules/api/scripts/models.js'), require('node_modules/api/scripts/hermes.js'))); | |
}, | |
'node_modules/views/scripts/popup.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (Observable, device) { | |
function Popup(width, height, optionsOrClassName) { | |
Observable.call(this); | |
this.width = width; | |
this.height = height; | |
var options = typeof optionsOrClassName === 'object' ? optionsOrClassName : typeof optionsOrClassName === 'string' ? { className: optionsOrClassName } : {}; | |
this.glue = !!options.glue; | |
if (this.glue) { | |
this.cssClass = options.className ? 'tooltip ' + options.className : 'tooltip'; | |
} else { | |
this.cssClass = options.className ? 'sp-popup ' + options.className : 'sp-popup'; | |
} | |
this.hideOnClick = options.hideOnClick !== undefined ? options.hideOnClick : true; | |
var self = this; | |
window.addEventListener('blur', function (evt) { | |
self.hide(); | |
}, false); | |
} | |
SP.inherit(Popup, Observable); | |
Popup.withContent = function (content, width, height, optionsOrClassName) { | |
var popup = new Popup(width, height, optionsOrClassName); | |
popup.setContent(content); | |
return popup; | |
}; | |
Popup.withText = function (text, optionsOrClassName) { | |
var options = typeof optionsOrClassName === 'object' ? optionsOrClassName : typeof optionsOrClassName === 'string' ? { className: optionsOrClassName } : {}; | |
if (!options.glue) { | |
options.className = options.className ? 'sp-text ' + options.className : 'sp-text'; | |
} else { | |
options.className = options.className ? 'text ' + options.className : 'text'; | |
} | |
var popup = new Popup(0, 0, options); | |
popup.setText(text); | |
return popup; | |
}; | |
Popup.prototype._update = function () { | |
if (!this.attachedTo) | |
return; | |
var arrowWidth = 6, padding = 10, minimumOverlap = 25, border = 2; | |
var node = this.getNode(); | |
node.style.width = this.width !== 0 ? this.width + 'px' : 'auto'; | |
node.style.height = this.height !== 0 ? this.height + 'px' : 'auto'; | |
var popup = node.getBoundingClientRect(); | |
var target = this.attachedTo.getBoundingClientRect(); | |
var midX = (target.left + target.right) / 2, midY = (target.top + target.bottom) / 2; | |
var arrow = this._arrowNode, arrowBorder = this._arrowNode.border, arrowSolid = this._arrowNode.solid; | |
var x, y; | |
var fitsVertical = Math.min(_viewportWidth - target.left, target.right) > minimumOverlap + padding, fitsAbove = target.top > popup.height + padding, fitsBelow = _viewportHeight - target.bottom > popup.height + padding; | |
if (fitsVertical && (fitsBelow || fitsAbove)) { | |
x = midX - popup.width / 2; | |
var min = Math.max(padding, target.left - popup.width + minimumOverlap), max = Math.min(_viewportWidth - popup.width - padding, target.right - minimumOverlap); | |
if (x > max) | |
x = max; | |
if (x < min) | |
x = min; | |
var arrowX; | |
if (arrowWidth + (minimumOverlap - padding) * 2 > popup.width) { | |
arrowX = (popup.width - arrowWidth) / 2; | |
} else { | |
arrowX = midX - x - border; | |
if (arrowX < minimumOverlap - padding) { | |
arrowX = minimumOverlap - padding; | |
} else if (arrowX > popup.width - minimumOverlap + padding) { | |
arrowX = popup.width - minimumOverlap + padding; | |
} | |
} | |
if (!this.glue) { | |
arrowBorder.style.left = arrowSolid.style.left = Math.floor(arrowX) + 'px'; | |
arrowBorder.style.top = arrowSolid.style.top = null; | |
} else { | |
arrow.style.left = Math.floor(arrowX) + 'px'; | |
arrow.style.top = null; | |
} | |
if ((_viewportHeight - target.bottom > target.top || !fitsAbove) && fitsBelow) { | |
y = target.bottom + padding + border; | |
node.className = this.cssClass + (this.glue ? ' bottom' : ' sp-popup-below'); | |
} else { | |
y = target.top - padding - popup.height - border; | |
node.className = this.cssClass + (this.glue ? ' top' : ' sp-popup-above'); | |
} | |
} else { | |
y = midY - popup.height / 2; | |
var min = Math.max(padding, target.top - popup.height + minimumOverlap), max = Math.min(_viewportHeight - popup.height - padding, target.bottom - minimumOverlap); | |
if (y > max) | |
y = max; | |
if (y < min) | |
y = min; | |
var arrowY; | |
if (arrowWidth + (minimumOverlap - padding) * 2 > popup.height) { | |
arrowY = (popup.height - arrowWidth) / 2; | |
} else { | |
arrowY = midY - y - border; | |
if (arrowY < minimumOverlap - padding) { | |
arrowY = minimumOverlap - padding; | |
} else if (arrowY > popup.height - minimumOverlap + padding) { | |
arrowY = popup.height - minimumOverlap + padding; | |
} | |
} | |
if (!this.glue) { | |
arrowBorder.style.top = arrowSolid.style.top = Math.floor(arrowY) + 'px'; | |
arrowBorder.style.left = arrowSolid.style.left = null; | |
} else { | |
arrow.style.top = Math.floor(arrowY) + 'px'; | |
arrow.style.left = null; | |
} | |
var fitsLeft = target.left > popup.width + padding, fitsRight = _viewportWidth - target.right > popup.width + padding; | |
if ((_viewportWidth - target.right > target.left || !fitsLeft) && fitsRight) { | |
x = target.right + padding + border; | |
node.className = this.cssClass + (this.glue ? ' right' : ' sp-popup-right'); | |
} else if (fitsLeft) { | |
x = target.left - padding - popup.width - border; | |
node.className = this.cssClass + (this.glue ? ' left' : ' sp-popup-left'); | |
} else { | |
x = 0; | |
node.className = this.cssClass; | |
} | |
} | |
node.style.left = Math.ceil(_scrollX + x) + 'px'; | |
node.style.top = Math.ceil(_scrollY + y) + 'px'; | |
}; | |
Popup.prototype.getNode = function () { | |
if (this._node) | |
return this._node; | |
var node = document.createElement('div'); | |
node.className = this.cssClass; | |
node.style.visibility = 'hidden'; | |
var arrow, arrowBorder, arrowSolid; | |
if (this.glue) { | |
arrow = document.createElement('span'); | |
arrow.className = 'tooltip-arrow'; | |
node.appendChild(arrow); | |
} else { | |
arrowBorder = document.createElement('div'); | |
arrowBorder.className = 'sp-arrow-border'; | |
node.appendChild(arrowBorder); | |
arrowSolid = document.createElement('div'); | |
arrowSolid.className = 'sp-arrow-solid'; | |
node.appendChild(arrowSolid); | |
} | |
if (this.content) { | |
this.content.className = this.glue ? this.content.className + ' tooltip-inner' : this.content.className; | |
node.appendChild(this.content); | |
} | |
this._node = node; | |
this._arrowNode = this.glue ? arrow : { | |
solid: arrowSolid, | |
border: arrowBorder | |
}; | |
return node; | |
}; | |
Popup.prototype.hide = function (opt_delay) { | |
if (!this.attachedTo) | |
return; | |
if (opt_delay) { | |
if (this._hideTimeout && this._hideTimeoutTime - +new Date() > opt_delay + 50) { | |
clearTimeout(this._hideTimeout); | |
} else if (this._hideTimeout) { | |
return; | |
} | |
var self = this; | |
this._hideTimeout = setTimeout(function () { | |
self.hide(); | |
}, opt_delay); | |
this._hideTimeoutTime = +new Date() + opt_delay; | |
return; | |
} | |
if (this._hideTimeout) { | |
clearTimeout(this._hideTimeout); | |
delete this._hideTimeout; | |
delete this._hideTimeoutTime; | |
} | |
if (this.hideOnClick) | |
document.removeEventListener('mousedown', this._clickToHideHandler, true); | |
if (this._isInDOM) { | |
this._isInDOM = false; | |
this._node.parentNode.removeChild(this._node); | |
} | |
this._node.style.visibility = 'hidden'; | |
delete this.attachedTo; | |
_unregister(this); | |
}; | |
Popup.prototype.dispose = function () { | |
_unregister(this); | |
if (this._clickToHideHandler) { | |
document.removeEventListener('mousedown', this._clickToHideHandler, true); | |
delete this._clickToHideHandler; | |
} | |
if (this.content && this.content.parentNode == this._node) { | |
this._node.removeChild(this.content); | |
} | |
if (this._node && this._node.parentNode) { | |
this._node.parentNode.removeChild(this._node); | |
} | |
if (this._hideTimeout) { | |
clearTimeout(this._hideTimeout); | |
delete this._hideTimeout; | |
delete this._hideTimeoutTime; | |
} | |
delete this.attachedTo; | |
delete this.content; | |
delete this._node; | |
delete this._arrowNode; | |
delete this._textContainer; | |
}; | |
Popup.prototype.resize = function (width, height) { | |
this.width = width; | |
this.height = height; | |
this._update(); | |
}; | |
Popup.prototype.setContent = function (content, opt_width, opt_height) { | |
if (content == this.content) | |
return; | |
if (this.content) { | |
this._node.removeChild(this.content); | |
} | |
this.content = content; | |
if (this._node) { | |
this.content.className = this.glue ? this.content.className + ' tooltip-inner' : this.content.className; | |
this._node.appendChild(content); | |
} | |
if (opt_width && opt_height) { | |
this.resize(opt_width, opt_height); | |
} | |
}; | |
Popup.prototype.setText = function (text, opt_maxWidth) { | |
var container = this._textContainer; | |
if (!container) { | |
container = document.createElement('span'); | |
this._textContainer = container; | |
} | |
container.textContent = text; | |
var node = this.getNode(); | |
node.style.width = (opt_maxWidth || 200) + 'px'; | |
this.setContent(container); | |
var rect; | |
if (!node.parentNode) { | |
document.body.appendChild(node); | |
rect = container.getBoundingClientRect(); | |
document.body.removeChild(node); | |
} | |
rect = rect || container.getBoundingClientRect(); | |
if (!this.glue) { | |
this.resize(Math.ceil(rect.width), Math.ceil(rect.height)); | |
} | |
}; | |
Popup.prototype.showFor = function (attachTo) { | |
if (attachTo == this.attachedTo) { | |
if (this._hideTimeout) { | |
clearTimeout(this._hideTimeout); | |
delete this._hideTimeout; | |
delete this._hideTimeoutTime; | |
} | |
return; | |
} | |
var node = this.getNode(); | |
if (this.attachedTo) | |
this.hide(); | |
this.attachedTo = attachTo; | |
document.body.appendChild(node); | |
this._isInDOM = true; | |
_register(this); | |
this._update(); | |
node.style.visibility = 'visible'; | |
var self = this; | |
if (this.hideOnClick && !this._clickToHideHandler) { | |
this._clickToHideHandler = function (evt) { | |
var t = evt.target, inside = false; | |
while (t) { | |
if (t == self._node) { | |
inside = true; | |
break; | |
} | |
t = t.parentNode; | |
} | |
if (!inside) { | |
self.dispatchEvent('hiddenOnClick'); | |
self.hide(); | |
} | |
}; | |
} | |
if (this.hideOnClick && !this._clickLinkHandler) { | |
this._clickLinkHandler = function (evt) { | |
var t = evt.target, isLink = false; | |
while (t && t !== document) { | |
if (t == self._node) { | |
break; | |
} | |
if (t.getAttribute('href')) { | |
isLink = true; | |
} | |
t = t.parentNode; | |
} | |
if (isLink) { | |
self.dispatchEvent('hiddenOnClick'); | |
self.hide(); | |
} | |
}; | |
} | |
if (this.hideOnClick) { | |
setTimeout(function () { | |
if (!self._node.parentNode) | |
return; | |
document.addEventListener('mousedown', self._clickToHideHandler, true); | |
document.addEventListener('click', self._clickLinkHandler, true); | |
}, 0); | |
} | |
}; | |
var _popups = []; | |
function _register(popup) { | |
if (_popups.indexOf(popup) >= 0) | |
return; | |
if (!_popups.length) { | |
_viewportHandler(); | |
window.addEventListener('resize', _viewportHandler); | |
window.addEventListener('scroll', _viewportHandler); | |
} | |
_popups.push(popup); | |
} | |
function _unregister(popup) { | |
var index = _popups.indexOf(popup); | |
if (index >= 0) { | |
_popups.splice(index, 1); | |
if (!_popups.length) { | |
window.removeEventListener('resize', _viewportHandler); | |
window.removeEventListener('scroll', _viewportHandler); | |
} | |
} | |
} | |
var _viewportWidth, _viewportHeight, _scrollX, _scrollY; | |
function _viewportHandler() { | |
var root = document.documentElement; | |
_viewportWidth = root.clientWidth; | |
_viewportHeight = root.clientHeight; | |
_scrollX = window.pageXOffset || root.scrollLeft; | |
_scrollY = window.pageYOffset || root.scrollTop; | |
for (var i = 0; i < _popups.length; i++) { | |
_popups[i]._update(); | |
} | |
} | |
exports.Popup = Popup; | |
}(require('node_modules/api/scripts/models.js').Observable, require('node_modules/views/scripts/utils/device.js'))); | |
}, | |
'node_modules/views/scripts/utils/logger.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (app, session) { | |
'use strict'; | |
var CONSOLE_METHOD = { | |
DEBUG: 'debug', | |
LOG: 'log', | |
WARN: 'warn', | |
ERROR: 'error' | |
}; | |
var loggers = {}; | |
function Logger(tag, context) { | |
this.tag = tag; | |
this.context = context; | |
this.timerData = {}; | |
this.timerOptions = {}; | |
this.inRolloutPercentage = true; | |
this.setLogOutputLevel(); | |
} | |
Logger.forTag = function (tag, context) { | |
if (typeof tag != 'string' || tag.length < 1) { | |
throw 'Improper tag name.'; | |
} | |
var returnLogger = loggers[tag]; | |
if (!returnLogger) { | |
returnLogger = new Logger(tag, context); | |
loggers[tag] = returnLogger; | |
} | |
return returnLogger; | |
}; | |
Logger.OUTPUT_LEVEL = { | |
DEBUG: 4, | |
LOG: 3, | |
INFO: 2, | |
ERROR: 1, | |
NONE: 0 | |
}; | |
Logger.prototype.setLogOutputLevel = function (logOutputLevel) { | |
var logOutputGobalOverride = null; | |
if (logOutputGobalOverride) { | |
this.logOutputLevel = logOutputGobalOverride; | |
} else if (logOutputLevel === undefined || logOutputLevel < Logger.OUTPUT_LEVEL.NONE || logOutputLevel > Logger.OUTPUT_LEVEL.DEBUG) { | |
var suffix = 'spotify.net'; | |
var hostname = window.location.hostname; | |
if (hostname.indexOf(suffix, hostname.length - suffix.length) !== -1) { | |
this.logOutputLevel = Logger.OUTPUT_LEVEL.ERROR; | |
} else { | |
this.logOutputLevel = Logger.OUTPUT_LEVEL.NONE; | |
} | |
} else { | |
this.logOutputLevel = logOutputLevel; | |
} | |
}; | |
Logger._testVersion = 'base'; | |
Logger.setTestVersion = function (testVersion) { | |
if (typeof testVersion != 'string' || testVersion.length < 1) { | |
throw 'Improper test name.'; | |
} | |
Logger._testVersion = testVersion; | |
}; | |
Logger.prototype.setTestRollout = function (percentage) { | |
var self = this; | |
this.inRolloutPercentage = false; | |
if (percentage > 0) { | |
session.load('testGroup').done(function (s) { | |
var tg = parseInt(s.testGroup, 10); | |
if (tg <= percentage * 10) { | |
self.inRolloutPercentage = true; | |
} | |
}); | |
} | |
}; | |
var ALLOWED_EVENTS = { | |
USER_HOLD_TIMER: 'user:hold', | |
USER_HIT: 'user:hit', | |
USER_SELECT: 'user:select', | |
USER_HOVER: 'user:hover', | |
USER_IMPRESSION: 'user:impression', | |
INFO_TIMER_DEFAULT: 'info:timer', | |
INFO_STATE_LOAD_TIMER: 'info:state_load_timer', | |
INFO_DEFAULT: 'info:default', | |
INFO_WARN: 'info:warn', | |
ERROR_DEFAULT: 'error:user_action_fail', | |
ERROR_USER_ACTION_FAIL: 'error:user_action_fail', | |
ERROR_RENDER_FAIL: 'error:render_fail', | |
DEBUG: 'debug' | |
}; | |
var C = CONSOLE_METHOD, O = Logger.OUTPUT_LEVEL, E = ALLOWED_EVENTS; | |
Logger.prototype.debug = function (eventVersion, data, opt_context) { | |
return this._log(C.DEBUG, E.DEBUG, O.DEBUG, eventVersion, data, opt_context, true, true); | |
}; | |
Logger.prototype.log = function (eventVersion, data, opt_context) { | |
return this._log(C.LOG, E.DEBUG, O.LOG, eventVersion, data, opt_context, true); | |
}; | |
Logger.prototype.userHit = function (eventVersion, data, opt_context) { | |
return this._log(C.LOG, E.USER_HIT, O.INFO, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.userSelect = function (eventVersion, data, opt_context) { | |
return this._log(C.LOG, E.USER_SELECT, O.INFO, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.userHover = function (eventVersion, data, opt_context) { | |
return this._log(C.LOG, E.USER_HOVER, O.INFO, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.userImpression = function (eventVersion, data, opt_context) { | |
return this._log(C.LOG, E.USER_IMPRESSION, O.INFO, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.info = function (eventVersion, data, opt_context) { | |
return this._log(C.LOG, E.INFO_DEFAULT, O.INFO, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.infoWarn = function (eventVersion, data, opt_context) { | |
return this._log(C.WARN, E.INFO_WARN, O.INFO, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.error = function (eventVersion, data, opt_context) { | |
return this._log(C.ERROR, E.ERROR_DEFAULT, O.ERROR, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.errorUserActionFail = function (eventVersion, data, opt_context) { | |
return this._log(C.ERROR, E.ERROR_USER_ACTION_FAIL, O.ERROR, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.errorRenderFail = function (eventVersion, data, opt_context) { | |
return this._log(C.ERROR, E.ERROR_RENDER_FAIL, O.ERROR, eventVersion, data, opt_context); | |
}; | |
Logger.prototype.startHoldTimer = function (eventVersion, data, opt_context) { | |
this._startTimer(eventVersion, data, E.USER_HOLD_TIMER, opt_context); | |
}; | |
Logger.prototype.startTimer = function (eventVersion, data, opt_context) { | |
this._startTimer(eventVersion, data, E.INFO_TIMER_DEFAULT, opt_context); | |
}; | |
Logger.prototype.startStateLoadTimer = function (eventVersion, data, opt_context) { | |
this._startTimer(eventVersion, data, E.INFO_STATE_LOAD_TIMER, opt_context); | |
}; | |
Logger.prototype.startDebugTimer = function (eventVersion, data, opt_context) { | |
this._startTimer(eventVersion, data, E.DEBUG_TIMER, opt_context); | |
}; | |
Logger.prototype.hasTimer = function (eventVersion) { | |
return this.timerData[eventVersion] ? true : false; | |
}; | |
Logger.prototype.overrideStartTime = function (eventVersion, newStartTime) { | |
this.timerData[eventVersion].timerStart = newStartTime; | |
}; | |
Logger.prototype.cancelTimer = function (eventVersion) { | |
delete this.timerData[eventVersion]; | |
delete this.timerOptions[eventVersion]; | |
}; | |
Logger.prototype.tickTimer = function (eventVersion, tickName) { | |
if (!this.timerData[eventVersion]) { | |
return this.error('cannot_tick_unstarted_timer', { timerName: eventVersion }); | |
} | |
tickName = 'timer_tick_' + tickName; | |
var data = this.timerData[eventVersion]; | |
var tickDiff = new Date().getTime() - data.timerStart; | |
data[tickName] = tickDiff; | |
return true; | |
}; | |
Logger.prototype.endTimer = function (eventVersion, opt_overrideTotalTime) { | |
if (!this.timerData[eventVersion]) { | |
return this.error('cannot_end_unstarted_timer', { timerName: eventVersion }); | |
} | |
var data = this.timerData[eventVersion]; | |
if (typeof opt_overrideTotalTime == 'number') { | |
data['timer_total_time'] = opt_overrideTotalTime; | |
} else { | |
var timerEnd = new Date().getTime(); | |
data['timer_total_time'] = timerEnd - data.timerStart; | |
} | |
delete data.timerStart; | |
var opts = this.timerOptions[eventVersion]; | |
var isDebug = opts.timerEvent == ALLOWED_EVENTS.DEBUG_TIMER; | |
var retVal = this._log(isDebug ? CONSOLE_METHOD.DEBUG : CONSOLE_METHOD.LOG, opts.timerEvent, isDebug ? Logger.OUTPUT_LEVEL.DEBUG : Logger.OUTPUT_LEVEL.INFO, eventVersion, data, opts.context, isDebug); | |
this.cancelTimer(eventVersion); | |
return retVal; | |
}; | |
Logger.prototype.addDataToTimer = function (eventVersion, propName, propValue) { | |
if (!this.timerData[eventVersion]) { | |
return this.error('cannot_add_data_to_unstarted_timer', { timerName: eventVersion }); | |
} | |
var tickNamePattern = /^timer_tick_\d*/; | |
if (propName == 'timerStart' || tickNamePattern.test(propName)) { | |
return this.error('cant_override_tick_info', { timerName: eventVersion }); | |
} else { | |
this.timerData[eventVersion][propName] = propValue; | |
} | |
}; | |
Logger.prototype._startTimer = function (eventVersion, data, timerEvent, opt_context) { | |
if (this.timerData[eventVersion]) { | |
return this.error('cannot_restart_timer', { timerName: eventVersion }); | |
} | |
data = data ? data : {}; | |
data.timerStart = new Date().getTime(); | |
this.timerData[eventVersion] = data; | |
this.timerOptions[eventVersion] = { | |
timerEvent: timerEvent, | |
context: opt_context | |
}; | |
return true; | |
}; | |
Logger.prototype._getErrorObject = function () { | |
try { | |
throw new Error(''); | |
} catch (err) { | |
return err; | |
} | |
}; | |
Logger.prototype._getBackendData = function (data) { | |
if (typeof data == 'string' || typeof data == 'number' || typeof data == 'boolean') { | |
return { data: data }; | |
} | |
if (!data) { | |
return {}; | |
} | |
if (typeof data != 'object') { | |
return { error: 'unparsable_data' }; | |
} | |
var backendData = {}; | |
for (var key in data) { | |
if (data.hasOwnProperty(key) && (typeof data[key] == 'string' || typeof data[key] == 'number' || typeof data[key] == 'boolean')) { | |
backendData[key] = data[key]; | |
} | |
} | |
return backendData; | |
}; | |
Logger.prototype._log = function (consoleMethod, event, minOutputLevel, eventVersion, data, opt_context, opt_debugOnly, opt_printStacktrace) { | |
var sole = typeof console !== 'undefined'; | |
var context; | |
if (opt_context) | |
context = opt_context; | |
else | |
context = this.context ? this.context : ''; | |
event = event ? event : ''; | |
eventVersion = eventVersion ? eventVersion : ''; | |
if (!opt_debugOnly && this.inRolloutPercentage) { | |
var backendData = this._getBackendData(data); | |
var promise = app.clientEvent(context, event, eventVersion, Logger._testVersion, backendData); | |
promise.fail(function () { | |
sole && console.error('could_not_log_to_backend'); | |
}); | |
} | |
if (!sole || this.logOutputLevel < minOutputLevel) { | |
return false; | |
} | |
var stackTrace = ''; | |
if (opt_printStacktrace) { | |
var err = this._getErrorObject(); | |
stackTrace = '{no stack-trace available}'; | |
if (err.stack) { | |
stackTrace = err.stack.split('\n').slice(1); | |
} | |
} | |
var consoleFunction = console[consoleMethod] ? console[consoleMethod] : console.log; | |
if (typeof consoleFunction == 'object') { | |
consoleFunction = Function.prototype.bind.call(consoleFunction, console); | |
} | |
data = data ? data : ''; | |
consoleFunction.apply(console, [ | |
'[' + this.tag + ']', | |
context, | |
event, | |
eventVersion, | |
data, | |
stackTrace | |
]); | |
return true; | |
}; | |
exports.Logger = Logger; | |
}(require('node_modules/api/scripts/models.js').application, require('node_modules/api/scripts/models.js').session)); | |
}, | |
'node_modules/views/scripts/utils/frame.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var requestFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { | |
return setTimeout(callback, 1000 / 60); | |
}; | |
var callbacks = []; | |
var iterator = function (time) { | |
if (time == null) | |
time = +new Date(); | |
var split = callbacks.splice(0, callbacks.length); | |
for (var i = 0, l = split.length; i < l; i++) | |
split[i](time); | |
}; | |
var request = function (callback) { | |
if (callbacks.push(callback) === 1) | |
requestFrame.call(window, iterator); | |
}; | |
var cancel = function (match) { | |
var io = callbacks.indexOf(match); | |
if (io > -1) | |
callbacks.splice(io, 1); | |
}; | |
var Queue = function () { | |
this.list = []; | |
}; | |
Queue.prototype.push = function (fn, ctx) { | |
return add.call(this, 'push', fn, ctx); | |
}; | |
Queue.prototype.unshift = function (fn, ctx) { | |
return add.call(this, 'unshift', fn, ctx); | |
}; | |
Queue.prototype.count = function () { | |
return this.list.length; | |
}; | |
var add = function (how, fn, ctx) { | |
var list = this.list; | |
var bound = function () { | |
return fn.apply(ctx, arguments); | |
}; | |
var next = function () { | |
var nextItem = list.shift(); | |
if (nextItem) | |
nextItem(); | |
if (list.length) | |
request(next); | |
}; | |
if (list[how](bound) === 1) | |
request(next); | |
return function () { | |
var io = list.indexOf(bound); | |
if (io > -1) | |
list.splice(io, 1); | |
}; | |
}; | |
var throttle = function (fn, ctx) { | |
var queued = false; | |
var args; | |
return function () { | |
args = arguments; | |
if (!queued) { | |
queued = true; | |
request(function () { | |
queued = false; | |
fn.apply(ctx, args); | |
}); | |
} | |
}; | |
}; | |
exports.throttle = throttle; | |
exports.request = request; | |
exports.cancel = cancel; | |
exports.Queue = Queue; | |
}, | |
'node_modules/views/arb.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/arb.loc/strings/image.lang', { | |
'Starred': 'Starred', | |
'Toplist': 'Top tracks' | |
}); | |
}, | |
'node_modules/views/bn.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/bn.loc/strings/image.lang', { | |
'Starred': 'Starred', | |
'Toplist': 'Top tracks' | |
}); | |
}, | |
'node_modules/views/de.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/de.loc/strings/image.lang', { | |
'Starred': 'Starred', | |
'Toplist': 'Top-Titel' | |
}); | |
}, | |
'node_modules/views/en.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/en.loc/strings/image.lang', { | |
'Starred': 'Starred', | |
'Toplist': 'Top tracks' | |
}); | |
}, | |
'node_modules/views/fi.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/fi.loc/strings/image.lang', { | |
'Starred': 'T\xE4hdell\xE4 merkityt', | |
'Toplist': 'Soitetuimmat kappaleet' | |
}); | |
}, | |
'node_modules/views/fr.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/fr.loc/strings/image.lang', { | |
'Starred': 'S\xE9lection', | |
'Toplist': 'Top titres' | |
}); | |
}, | |
'node_modules/views/el.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/el.loc/strings/image.lang', { | |
'Starred': '\u0391\u03B3\u03B1\u03C0\u03B7\u03BC\u03AD\u03BD\u03B1', | |
'Toplist': '\u039A\u03BF\u03C1\u03C5\u03C6\u03B1\u03AF\u03B1 \u03C4\u03C1\u03B1\u03B3\u03BF\u03CD\u03B4\u03B9\u03B1' | |
}); | |
}, | |
'node_modules/views/es.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/es.loc/strings/image.lang', { | |
'Starred': 'Favoritos', | |
'Toplist': 'Canciones m\xE1s escuchadas' | |
}); | |
}, | |
'node_modules/views/es-la.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/es-la.loc/strings/image.lang', { | |
'Starred': 'Seleccionadas', | |
'Toplist': 'Canciones favoritas' | |
}); | |
}, | |
'node_modules/views/es-419.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/es-419.loc/strings/image.lang', { | |
'Starred': 'Seleccionadas', | |
'Toplist': 'Canciones favoritas' | |
}); | |
}, | |
'node_modules/views/hi.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/hi.loc/strings/image.lang', { | |
'Starred': 'Starred', | |
'Toplist': 'Top tracks' | |
}); | |
}, | |
'node_modules/views/hu.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/hu.loc/strings/image.lang', { | |
'Starred': 'Megcsillagozott', | |
'Toplist': 'N\xE9pszer\u0171 dalok' | |
}); | |
}, | |
'node_modules/views/id.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/id.loc/strings/image.lang', { | |
'Starred': 'Diberi bintang', | |
'Toplist': 'Lagu teratas' | |
}); | |
}, | |
'node_modules/views/ja.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/ja.loc/strings/image.lang', { | |
'Starred': '\u30B9\u30BF\u30FC', | |
'Toplist': '\u30C8\u30C3\u30D7\u66F2' | |
}); | |
}, | |
'node_modules/views/ko.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/ko.loc/strings/image.lang', { | |
'Starred': 'Starred', | |
'Toplist': 'Top tracks' | |
}); | |
}, | |
'node_modules/views/nl.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/nl.loc/strings/image.lang', { | |
'Starred': 'Favorieten', | |
'Toplist': 'Topnummers' | |
}); | |
}, | |
'node_modules/views/pl.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/pl.loc/strings/image.lang', { | |
'Starred': 'Oznaczone gwiazdk\u0105', | |
'Toplist': 'Najlepsze utwory' | |
}); | |
}, | |
'node_modules/views/pt-br.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/pt-br.loc/strings/image.lang', { | |
'Starred': 'Favoritos', | |
'Toplist': 'As mais tocadas' | |
}); | |
}, | |
'node_modules/views/it.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/it.loc/strings/image.lang', { | |
'Starred': 'Preferiti', | |
'Toplist': 'Brani top' | |
}); | |
}, | |
'node_modules/views/ro.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/ro.loc/strings/image.lang', { | |
'Starred': 'Marcat cu stea', | |
'Toplist': 'Melodii de top' | |
}); | |
}, | |
'node_modules/views/ru.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/ru.loc/strings/image.lang', { | |
'Starred': '\u0418\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0435', | |
'Toplist': '\u041B\u0443\u0447\u0448\u0438\u0435 \u0442\u0440\u0435\u043A\u0438' | |
}); | |
}, | |
'node_modules/views/ta.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/ta.loc/strings/image.lang', { | |
'Starred': 'Starred', | |
'Toplist': 'Top tracks' | |
}); | |
}, | |
'node_modules/views/th.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/th.loc/strings/image.lang', { | |
'Starred': '\u0E43\u0E2B\u0E49\u0E04\u0E30\u0E41\u0E19\u0E19\u0E41\u0E25\u0E49\u0E27', | |
'Toplist': '\u0E41\u0E17\u0E23\u0E47\u0E01\u0E2D\u0E31\u0E19\u0E14\u0E31\u0E1A\u0E15\u0E49\u0E19\u0E46' | |
}); | |
}, | |
'node_modules/views/tr.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/tr.loc/strings/image.lang', { | |
'Starred': 'Y\u0131ld\u0131zl\u0131lar', | |
'Toplist': 'En \xE7ok dinlenen par\xE7alar' | |
}); | |
}, | |
'node_modules/views/zh-hant.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/zh-hant.loc/strings/image.lang', { | |
'Starred': '\u5DF2\u661F\u8A55', | |
'Toplist': '\u7576\u7D05\u6B4C\u66F2' | |
}); | |
}, | |
'node_modules/views/zsm.loc/strings/image.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/zsm.loc/strings/image.lang', { | |
'Starred': 'Dibintangkan', | |
'Toplist': 'Lagu paling popular' | |
}); | |
}, | |
'node_modules/views/arb.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/arb.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Follow', | |
'ButtonSubscribeArtist': 'Follow Artist', | |
'ButtonSubscribePlaylist': 'Follow Playlist', | |
'ButtonSubscribeUser': 'Follow User', | |
'ButtonSubscribed': 'Following', | |
'ButtonSubscribedArtist': 'Following Artist', | |
'ButtonSubscribedPlaylist': 'Following Playlist', | |
'ButtonSubscribedUser': 'Following User', | |
'ButtonUnsubscribe': 'Unfollow', | |
'ButtonUnsubscribeArtist': 'Unfollow Artist', | |
'ButtonUnsubscribePlaylist': 'Unfollow Playlist', | |
'ButtonUnsubscribeUser': 'Unfollow User', | |
'ButtonShare': 'Share\u2026', | |
'ButtonStartRadio': 'Start Radio', | |
'PopupPlaylistSubscribeCancel': 'No', | |
'PopupPlaylistSubscribeConfirm': 'Okay', | |
'PopupPlaylistSubscribeLine1': 'You\'re now also following {0} because you followed this playlist.', | |
'PopupPlaylistSubscribeLine2': 'Continue to follow this user?', | |
'PopupPlaylistSuggestFollow': '{0} created this playlist. To {2} more music from {1}, just hit {3}.', | |
'PopupAviciiAutoFollow': 'You are now following Avicii because you followed this playlist. Keep following Avicii to get notified about future releases!', | |
'DiscoverAppName': 'Discover', | |
'Followers': 'Followers', | |
'SaveToYourMusic': 'Save', | |
'RemoveFromYourMusic': 'Remove', | |
'SavedToYourMusic': 'Saved', | |
'User': 'User', | |
'Artist': 'Artist', | |
'Album': 'Album', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/bn.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/bn.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Follow', | |
'ButtonSubscribeArtist': 'Follow Artist', | |
'ButtonSubscribePlaylist': 'Follow Playlist', | |
'ButtonSubscribeUser': 'Follow User', | |
'ButtonSubscribed': 'Following', | |
'ButtonSubscribedArtist': 'Following Artist', | |
'ButtonSubscribedPlaylist': 'Following Playlist', | |
'ButtonSubscribedUser': 'Following User', | |
'ButtonUnsubscribe': 'Unfollow', | |
'ButtonUnsubscribeArtist': 'Unfollow Artist', | |
'ButtonUnsubscribePlaylist': 'Unfollow Playlist', | |
'ButtonUnsubscribeUser': 'Unfollow User', | |
'ButtonShare': 'Share\u2026', | |
'ButtonStartRadio': 'Start Radio', | |
'PopupPlaylistSubscribeCancel': 'No', | |
'PopupPlaylistSubscribeConfirm': 'Okay', | |
'PopupPlaylistSubscribeLine1': 'You\'re now also following {0} because you followed this playlist.', | |
'PopupPlaylistSubscribeLine2': 'Continue to follow this user?', | |
'PopupPlaylistSuggestFollow': '{0} created this playlist. To {2} more music from {1}, just hit {3}.', | |
'PopupAviciiAutoFollow': 'You are now following Avicii because you followed this playlist. Keep following Avicii to get notified about future releases!', | |
'DiscoverAppName': 'Discover', | |
'Followers': 'Followers', | |
'SaveToYourMusic': 'Save', | |
'RemoveFromYourMusic': 'Remove', | |
'SavedToYourMusic': 'Saved', | |
'User': 'User', | |
'Artist': 'Artist', | |
'Album': 'Album', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/de.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/de.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Folgen', | |
'ButtonSubscribeArtist': 'K\xFCnstler folgen', | |
'ButtonSubscribePlaylist': 'Playlist folgen', | |
'ButtonSubscribeUser': 'Benutzer folgen', | |
'ButtonSubscribed': 'Folge ich', | |
'ButtonSubscribedArtist': 'Du folgst diesem K\xFCnstler', | |
'ButtonSubscribedPlaylist': 'Folge Playlist ', | |
'ButtonSubscribedUser': 'Folge Benutzer', | |
'ButtonUnsubscribe': 'Nicht folgen', | |
'ButtonUnsubscribeArtist': 'K\xFCnstler nicht mehr folgen', | |
'ButtonUnsubscribePlaylist': 'Playlist nicht mehr folgen', | |
'ButtonUnsubscribeUser': 'Benutzer nicht mehr folgen', | |
'ButtonShare': 'Teilen\u2026', | |
'ButtonStartRadio': 'Radio starten', | |
'PopupPlaylistSubscribeCancel': 'Nein', | |
'PopupPlaylistSubscribeConfirm': 'OK', | |
'PopupPlaylistSubscribeLine1': 'Da du dieser Playlist folgst, folgst du jetzt auch {0}.', | |
'PopupPlaylistSubscribeLine2': 'Diesem Benutzer weiterhin folgen?', | |
'PopupPlaylistSuggestFollow': '{0} hat diese Playlist erstellt. F\xFCr mehr Musik von {1}, hol dir {2} zu Hilfe und klick einfach {3}.', | |
'PopupAviciiAutoFollow': 'Du folgst Avicii, weil du dieser Playlist folgst. Wenn du Avicii weiter folgst, bekommst du automatische Benachrichtigungen, sobald er etwas Neues releaset!', | |
'DiscoverAppName': 'Entdecken', | |
'Followers': 'Followers', | |
'SaveToYourMusic': 'Speichern', | |
'RemoveFromYourMusic': 'Entfernen', | |
'SavedToYourMusic': 'Gespeichert', | |
'User': 'Benutzer', | |
'Artist': 'K\xFCnstler', | |
'Album': 'Album', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/en.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/en.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Follow', | |
'ButtonSubscribeArtist': 'Follow Artist', | |
'ButtonSubscribePlaylist': 'Follow Playlist', | |
'ButtonSubscribeUser': 'Follow User', | |
'ButtonSubscribed': 'Following', | |
'ButtonSubscribedArtist': 'Following Artist', | |
'ButtonSubscribedPlaylist': 'Following Playlist', | |
'ButtonSubscribedUser': 'Following User', | |
'ButtonUnsubscribe': 'Unfollow', | |
'ButtonUnsubscribeArtist': 'Unfollow Artist', | |
'ButtonUnsubscribePlaylist': 'Unfollow Playlist', | |
'ButtonUnsubscribeUser': 'Unfollow User', | |
'ButtonShare': 'Share\u2026', | |
'ButtonStartRadio': 'Start Radio', | |
'PopupPlaylistSubscribeCancel': 'No', | |
'PopupPlaylistSubscribeConfirm': 'Okay', | |
'PopupPlaylistSubscribeLine1': 'You\'re now also following {0} because you followed this playlist.', | |
'PopupPlaylistSubscribeLine2': 'Continue to follow this user?', | |
'PopupPlaylistSuggestFollow': '{0} created this playlist. To {2} more music from {1}, just hit {3}.', | |
'PopupAviciiAutoFollow': 'You are now following Avicii because you followed this playlist. Keep following Avicii to get notified about future releases!', | |
'DiscoverAppName': 'Discover', | |
'Followers': 'Followers', | |
'SaveToYourMusic': 'Save', | |
'RemoveFromYourMusic': 'Remove', | |
'SavedToYourMusic': 'Saved', | |
'User': 'User', | |
'Artist': 'Artist', | |
'Album': 'Album', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/fi.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/fi.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Seuraa', | |
'ButtonSubscribeArtist': 'Seuraa artistia', | |
'ButtonSubscribePlaylist': 'Seuraa soittolistaa', | |
'ButtonSubscribeUser': 'Seuraa k\xE4ytt\xE4j\xE4\xE4', | |
'ButtonSubscribed': 'Seuratut', | |
'ButtonSubscribedArtist': 'Seurattava artisti', | |
'ButtonSubscribedPlaylist': 'Seurattava soittolista', | |
'ButtonSubscribedUser': 'Seurattava k\xE4ytt\xE4j\xE4', | |
'ButtonUnsubscribe': 'Lopeta seuraaminen', | |
'ButtonUnsubscribeArtist': 'Lopeta artistin seuraaminen', | |
'ButtonUnsubscribePlaylist': 'Lopeta soittolistan seuraaminen', | |
'ButtonUnsubscribeUser': 'Lopeta k\xE4ytt\xE4j\xE4n seuraaminen', | |
'ButtonShare': 'Jaa...', | |
'ButtonStartRadio': 'K\xE4ynnist\xE4 radio', | |
'PopupPlaylistSubscribeCancel': 'EI', | |
'PopupPlaylistSubscribeConfirm': 'OK', | |
'PopupPlaylistSubscribeLine1': 'Seuraat nyt my\xF6s kohdetta {0}, koska seuraat t\xE4t\xE4 soittolistaa. ', | |
'PopupPlaylistSubscribeLine2': 'Jatketaanko t\xE4m\xE4n k\xE4ytt\xE4j\xE4n seuraamista?', | |
'PopupPlaylistSuggestFollow': '{0} loi t\xE4m\xE4n soittolistan. Jos haluat {2} lis\xE4\xE4 musiikkia kohteesta {1}, valitse {3}.', | |
'PopupAviciiAutoFollow': 'Seuraat nyt Aviciita, koska seurasit t\xE4t\xE4 soittolistaa. Jatka Aviciin seuraamista, niin saat tietoja tulevista julkaisuista!', | |
'DiscoverAppName': 'L\xF6yd\xE4', | |
'Followers': 'Seuraajat', | |
'SaveToYourMusic': 'Tallenna', | |
'RemoveFromYourMusic': 'Poista', | |
'SavedToYourMusic': 'Tallennettu', | |
'User': 'K\xE4ytt\xE4j\xE4', | |
'Artist': 'Artisti', | |
'Album': 'Albumi', | |
'Playlist': 'Soittolista' | |
}); | |
}, | |
'node_modules/views/fr.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/fr.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Suivre', | |
'ButtonSubscribeArtist': 'Suivre l\'artiste', | |
'ButtonSubscribePlaylist': 'Suivre la playlist', | |
'ButtonSubscribeUser': 'Suivre l\'utilisateur', | |
'ButtonSubscribed': 'Suivi', | |
'ButtonSubscribedArtist': 'Artiste suivi', | |
'ButtonSubscribedPlaylist': 'Playlist suivie', | |
'ButtonSubscribedUser': 'Utilisateur suivi', | |
'ButtonUnsubscribe': 'Ne plus suivre', | |
'ButtonUnsubscribeArtist': 'Ne plus suivre l\'artiste', | |
'ButtonUnsubscribePlaylist': 'Ne plus suivre la playlist', | |
'ButtonUnsubscribeUser': 'Ne plus suivre l\'utilisateur', | |
'ButtonShare': 'Partager...', | |
'ButtonStartRadio': 'Lancer la radio', | |
'PopupPlaylistSubscribeCancel': 'Non', | |
'PopupPlaylistSubscribeConfirm': 'Oui', | |
'PopupPlaylistSubscribeLine1': 'Vous suivez maintenant {0} car vous suivez cette playlist.', | |
'PopupPlaylistSubscribeLine2': 'Continuer \xE0 suivre cet utilisateur\xA0?', | |
'PopupPlaylistSuggestFollow': '{0} a cr\xE9\xE9 cette playlist. Pour {2} plus de musique de {1}, cliquez sur {3}.', | |
'PopupAviciiAutoFollow': 'Vous suivez maintenant Avicii car vous suivez cette playlist. Continuez de le suivre pour rester inform\xE9 de ses nouveaut\xE9s\xA0!', | |
'DiscoverAppName': 'D\xE9couvrir', | |
'Followers': 'Abonn\xE9s', | |
'SaveToYourMusic': 'Enregistrer', | |
'RemoveFromYourMusic': 'Supprimer', | |
'SavedToYourMusic': 'Enregistr\xE9', | |
'User': 'Utilisateur', | |
'Artist': 'Artiste', | |
'Album': 'Album', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/el.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/el.loc/strings/buttons.lang', { | |
'ButtonSubscribe': '\u0391\u03BA\u03BF\u03BB\u03BF\u03CD\u03B8\u03B7\u03C3\u03B5', | |
'ButtonSubscribeArtist': '\u0391\u03BA\u03BF\u03BB\u03BF\u03CD\u03B8\u03B7\u03C3\u03B5 \u03BA\u03B1\u03BB\u03BB\u03B9\u03C4\u03AD\u03C7\u03BD\u03B7', | |
'ButtonSubscribePlaylist': '\u0391\u03BA\u03BF\u03BB\u03BF\u03CD\u03B8\u03B7\u03C3\u03B5 \u03BB\u03AF\u03C3\u03C4\u03B1', | |
'ButtonSubscribeUser': 'A\u03BA\u03BF\u03BB\u03BF\u03CD\u03B8\u03B7\u03C3\u03B7 \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7', | |
'ButtonSubscribed': '\u0386\u03C4\u03BF\u03BC\u03B1 \u03C0\u03BF\u03C5 \u03B1\u03BA\u03BF\u03BB\u03BF\u03C5\u03B8\u03BF\u03CD\u03BD\u03C4\u03B1\u03B9', | |
'ButtonSubscribedArtist': '\u039A\u03B1\u03BB\u03BB\u03B9\u03C4\u03AD\u03C7\u03BD\u03B7\u03C2 \u03C0\u03BF\u03C5 \u03B1\u03BA\u03BF\u03BB\u03BF\u03C5\u03B8\u03B5\u03AF\u03C4\u03B1\u03B9', | |
'ButtonSubscribedPlaylist': '\u039B\u03AF\u03C3\u03C4\u03B1 \u03C0\u03BF\u03C5 \u03B1\u03BA\u03BF\u03BB\u03BF\u03C5\u03B8\u03B5\u03AF\u03C4\u03B1\u03B9', | |
'ButtonSubscribedUser': '\u03A7\u03C1\u03AE\u03C3\u03C4\u03B7\u03C2 \u03C0\u03BF\u03C5 \u03B1\u03BA\u03BF\u03BB\u03BF\u03C5\u03B8\u03B5\u03AF\u03C4\u03B1\u03B9', | |
'ButtonUnsubscribe': '\u0386\u03C1\u03C3\u03B7 \u03B1\u03BA\u03BF\u03BB\u03BF\u03CD\u03B8\u03B7\u03C3\u03B7\u03C2', | |
'ButtonUnsubscribeArtist': '\u0386\u03C1\u03C3\u03B7 \u03B1\u03BA\u03BF\u03BB\u03BF\u03CD\u03B8\u03B7\u03C3\u03B7\u03C2 \u03BA\u03B1\u03BB\u03BB\u03B9\u03C4\u03AD\u03C7\u03BD\u03B7', | |
'ButtonUnsubscribePlaylist': '\u0386\u03C1\u03C3\u03B7 \u03B1\u03BA\u03BF\u03BB\u03BF\u03CD\u03B8\u03B7\u03C3\u03B7\u03C2 \u03BB\u03AF\u03C3\u03C4\u03B1\u03C2', | |
'ButtonUnsubscribeUser': '\u0386\u03C1\u03C3\u03B7 \u03B1\u03BA\u03BF\u03BB\u03BF\u03CD\u03B8\u03B7\u03C3\u03B7\u03C2 \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7', | |
'ButtonShare': '\u039A\u03BF\u03B9\u03BD\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7\u2026', | |
'ButtonStartRadio': '\u0388\u03BD\u03B1\u03C1\u03BE\u03B7 \u03C1\u03B1\u03B4\u03B9\u03BF\u03C6\u03CE\u03BD\u03BF\u03C5', | |
'PopupPlaylistSubscribeCancel': '\u038C\u03C7\u03B9', | |
'PopupPlaylistSubscribeConfirm': '\u0395\u03BD\u03C4\u03AC\u03BE\u03B5\u03B9', | |
'PopupPlaylistSubscribeLine1': '\u03A0\u03BB\u03AD\u03BF\u03BD \u03B1\u03BA\u03BF\u03BB\u03BF\u03C5\u03B8\u03B5\u03AF\u03C2 \u03C4\u03BF\u03BD \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7 {0} \u03B5\u03C0\u03B5\u03B9\u03B4\u03AE \u03AC\u03C1\u03C7\u03B9\u03C3\u03B5\u03C2 \u03BD\u03B1 \u03B1\u03BA\u03BF\u03BB\u03BF\u03C5\u03B8\u03B5\u03AF\u03C2 \u03B1\u03C5\u03C4\u03AE \u03C4\u03B7 \u03BB\u03AF\u03C3\u03C4\u03B1.', | |
'PopupPlaylistSubscribeLine2': '\u0398\u03B1 \u03C3\u03C5\u03BD\u03B5\u03C7\u03AF\u03C3\u03B5\u03B9\u03C2 \u03BD\u03B1 \u03B1\u03BA\u03BF\u03BB\u03BF\u03C5\u03B8\u03B5\u03AF\u03C2 \u03B1\u03C5\u03C4\u03CC\u03BD \u03C4\u03BF\u03BD \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7;', | |
'PopupPlaylistSuggestFollow': '{0} \u03B4\u03B7\u03BC\u03B9\u03BF\u03CD\u03C1\u03B3\u03B7\u03C3\u03B5 \u03B1\u03C5\u03C4\u03AE \u03C4\u03B7 \u03BB\u03AF\u03C3\u03C4\u03B1. \u0393\u03B9\u03B1 \u03BD\u03B1 {2} \u03C0\u03B5\u03C1\u03B9\u03C3\u03C3\u03CC\u03C4\u03B5\u03C1\u03B7 \u03BC\u03BF\u03C5\u03C3\u03B9\u03BA\u03AE \u03B1\u03C0\u03CC {1}, \u03B1\u03C0\u03BB\u03AC \u03C0\u03B1\u03C4\u03AE\u03C3\u03C4\u03B5 {3}.', | |
'PopupAviciiAutoFollow': '\u0391\u03BA\u03BF\u03BB\u03BF\u03C5\u03B8\u03B5\u03AF\u03C2 \u03C4\u03BF\u03BD Avicii \u03B5\u03C0\u03B5\u03B9\u03B4\u03AE \u03B1\u03BA\u03BF\u03BB\u03BF\u03CD\u03B8\u03B7\u03C3\u03B5\u03C2 \u03B1\u03C5\u03C4\u03AE \u03C4\u03B7 \u03BB\u03AF\u03C3\u03C4\u03B1. \u03A3\u03C5\u03BD\u03AD\u03C7\u03B9\u03C3\u03B5 \u03BD\u03B1 \u03B1\u03BA\u03BF\u03BB\u03BF\u03C5\u03B8\u03B5\u03AF\u03C2 \u03C4\u03BF\u03BD Avicii \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03B5\u03BD\u03B7\u03BC\u03B5\u03C1\u03CE\u03BD\u03B5\u03C3\u03B1\u03B9 \u03B3\u03B9\u03B1 \u03BC\u03B5\u03BB\u03BB\u03BF\u03BD\u03C4\u03B9\u03BA\u03AD\u03C2 \u03BA\u03C5\u03BA\u03BB\u03BF\u03C6\u03BF\u03C1\u03AF\u03B5\u03C2!', | |
'DiscoverAppName': '\u0391\u03BD\u03B1\u03BA\u03AC\u03BB\u03C5\u03C8\u03B5 ', | |
'Followers': '\u039F\u03C0\u03B1\u03B4\u03BF\u03AF', | |
'SaveToYourMusic': '\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7', | |
'RemoveFromYourMusic': '\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7', | |
'SavedToYourMusic': '\u0391\u03C0\u03BF\u03B8\u03B7\u03BA\u03B5\u03CD\u03C4\u03B7\u03BA\u03B5', | |
'User': '\u03A7\u03C1\u03AE\u03C3\u03C4\u03B7\u03C2', | |
'Artist': '\u039A\u03B1\u03BB\u03BB\u03B9\u03C4\u03AD\u03C7\u03BD\u03B7\u03C2', | |
'Album': '\u0386\u03BB\u03BC\u03C0\u03BF\u03C5\u03BC', | |
'Playlist': '\u039B\u03AF\u03C3\u03C4\u03B1' | |
}); | |
}, | |
'node_modules/views/es.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/es.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Seguir', | |
'ButtonSubscribeArtist': 'Seguir artista', | |
'ButtonSubscribePlaylist': 'Seguir playlist', | |
'ButtonSubscribeUser': 'Seguir al usuario', | |
'ButtonSubscribed': 'Siguiendo', | |
'ButtonSubscribedArtist': 'Artista que sigues', | |
'ButtonSubscribedPlaylist': 'Playlist que sigues', | |
'ButtonSubscribedUser': 'Usuario que sigues', | |
'ButtonUnsubscribe': 'No seguir', | |
'ButtonUnsubscribeArtist': 'Dejar de seguir al artista', | |
'ButtonUnsubscribePlaylist': 'Dejar de seguir la playlist', | |
'ButtonUnsubscribeUser': 'Dejar de seguir al usuario', | |
'ButtonShare': 'Compartir\u2026', | |
'ButtonStartRadio': 'Iniciar radio', | |
'PopupPlaylistSubscribeCancel': 'No', | |
'PopupPlaylistSubscribeConfirm': 'OK', | |
'PopupPlaylistSubscribeLine1': 'Ahora tambi\xE9n sigues a {0} porque sigues esta playlist.', | |
'PopupPlaylistSubscribeLine2': '\xBFQuieres continuar siguiendo a este usuario?', | |
'PopupPlaylistSuggestFollow': '{0} ha creado esta playlist. Para {2} m\xE1s m\xFAsica de {1}, simplemente dale a {3}.', | |
'PopupAviciiAutoFollow': 'Ahora est\xE1s siguiendo a Avicii porque le has dado a seguir a esta playlist. De esta forma, podr\xE1s mantenerte al d\xEDa de pr\xF3ximos lanzamientos.', | |
'DiscoverAppName': 'Descubrir', | |
'Followers': 'Seguidores', | |
'SaveToYourMusic': 'Guardar', | |
'RemoveFromYourMusic': 'Eliminar', | |
'SavedToYourMusic': 'Guardado/a', | |
'User': 'Usuario', | |
'Artist': 'Artista', | |
'Album': '\xC1lbum', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/es-la.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/es-la.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Seguir', | |
'ButtonSubscribeArtist': 'Seguir artista', | |
'ButtonSubscribePlaylist': 'Seguir playlist', | |
'ButtonSubscribeUser': 'Seguir a usuario', | |
'ButtonSubscribed': 'Siguiendo', | |
'ButtonSubscribedArtist': 'Siguiendo a artista', | |
'ButtonSubscribedPlaylist': 'Siguiendo playlist', | |
'ButtonSubscribedUser': 'Siguiendo a usuario', | |
'ButtonUnsubscribe': 'No seguir', | |
'ButtonUnsubscribeArtist': 'Dejar de seguir a artista', | |
'ButtonUnsubscribePlaylist': 'Dejar de seguir playlist', | |
'ButtonUnsubscribeUser': 'Dejar de seguir a usuario', | |
'ButtonShare': 'Compartir\u2026', | |
'ButtonStartRadio': 'Iniciar radio', | |
'PopupPlaylistSubscribeCancel': 'No', | |
'PopupPlaylistSubscribeConfirm': 'Ok', | |
'PopupPlaylistSubscribeLine1': 'Tambi\xE9n sigues a {0} debido a que seguiste su playlist. ', | |
'PopupPlaylistSubscribeLine2': '\xBFQuieres continuar siguiendo a este usuario?', | |
'PopupPlaylistSuggestFollow': '{0} cre\xF3 esta playlist. Para {2} m\xE1s m\xFAsica de {1}, simplemente pulsa {3}.', | |
'PopupAviciiAutoFollow': 'Ahora sigues a Avicii, ya que comenzaste a seguir esta playlist. \xA1Contin\xFAa siguiendo a Avicii para recibir notificaciones sobre sus lanzamientos futuros!', | |
'DiscoverAppName': 'Descubrir', | |
'Followers': 'Seguidores', | |
'SaveToYourMusic': 'Guardar', | |
'RemoveFromYourMusic': 'Eliminar', | |
'SavedToYourMusic': 'Guardado/a', | |
'User': 'Usuario', | |
'Artist': 'Artista', | |
'Album': '\xC1lbum', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/es-419.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/es-419.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Seguir', | |
'ButtonSubscribeArtist': 'Seguir artista', | |
'ButtonSubscribePlaylist': 'Seguir playlist', | |
'ButtonSubscribeUser': 'Seguir a usuario', | |
'ButtonSubscribed': 'Siguiendo', | |
'ButtonSubscribedArtist': 'Siguiendo a artista', | |
'ButtonSubscribedPlaylist': 'Siguiendo playlist', | |
'ButtonSubscribedUser': 'Siguiendo a usuario', | |
'ButtonUnsubscribe': 'No seguir', | |
'ButtonUnsubscribeArtist': 'Dejar de seguir a artista', | |
'ButtonUnsubscribePlaylist': 'Dejar de seguir playlist', | |
'ButtonUnsubscribeUser': 'Dejar de seguir a usuario', | |
'ButtonShare': 'Compartir\u2026', | |
'ButtonStartRadio': 'Iniciar radio', | |
'PopupPlaylistSubscribeCancel': 'No', | |
'PopupPlaylistSubscribeConfirm': 'Ok', | |
'PopupPlaylistSubscribeLine1': 'Tambi\xE9n sigues a {0} debido a que seguiste su playlist. ', | |
'PopupPlaylistSubscribeLine2': '\xBFQuieres continuar siguiendo a este usuario?', | |
'PopupPlaylistSuggestFollow': '{0} cre\xF3 esta playlist. Para {2} m\xE1s m\xFAsica de {1}, simplemente pulsa {3}.', | |
'PopupAviciiAutoFollow': 'Ahora sigues a Avicii, ya que comenzaste a seguir esta playlist. \xA1Contin\xFAa siguiendo a Avicii para recibir notificaciones sobre sus lanzamientos futuros!', | |
'DiscoverAppName': 'Descubrir', | |
'Followers': 'Seguidores', | |
'SaveToYourMusic': 'Guardar', | |
'RemoveFromYourMusic': 'Eliminar', | |
'SavedToYourMusic': 'Guardado/a', | |
'User': 'Usuario', | |
'Artist': 'Artista', | |
'Album': '\xC1lbum', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/hi.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/hi.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Follow', | |
'ButtonSubscribeArtist': 'Follow Artist', | |
'ButtonSubscribePlaylist': 'Follow Playlist', | |
'ButtonSubscribeUser': 'Follow User', | |
'ButtonSubscribed': 'Following', | |
'ButtonSubscribedArtist': 'Following Artist', | |
'ButtonSubscribedPlaylist': 'Following Playlist', | |
'ButtonSubscribedUser': 'Following User', | |
'ButtonUnsubscribe': 'Unfollow', | |
'ButtonUnsubscribeArtist': 'Unfollow Artist', | |
'ButtonUnsubscribePlaylist': 'Unfollow Playlist', | |
'ButtonUnsubscribeUser': 'Unfollow User', | |
'ButtonShare': 'Share\u2026', | |
'ButtonStartRadio': 'Start Radio', | |
'PopupPlaylistSubscribeCancel': 'No', | |
'PopupPlaylistSubscribeConfirm': 'Okay', | |
'PopupPlaylistSubscribeLine1': 'You\'re now also following {0} because you followed this playlist.', | |
'PopupPlaylistSubscribeLine2': 'Continue to follow this user?', | |
'PopupPlaylistSuggestFollow': '{0} created this playlist. To {2} more music from {1}, just hit {3}.', | |
'PopupAviciiAutoFollow': 'You are now following Avicii because you followed this playlist. Keep following Avicii to get notified about future releases!', | |
'DiscoverAppName': 'Discover', | |
'Followers': 'Followers', | |
'SaveToYourMusic': 'Save', | |
'RemoveFromYourMusic': 'Remove', | |
'SavedToYourMusic': 'Saved', | |
'User': 'User', | |
'Artist': 'Artist', | |
'Album': 'Album', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/hu.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/hu.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'K\xF6vet\xE9s', | |
'ButtonSubscribeArtist': 'El\u0151ad\xF3 k\xF6vet\xE9se', | |
'ButtonSubscribePlaylist': 'Lej\xE1tsz\xE1si lista k\xF6vet\xE9se', | |
'ButtonSubscribeUser': 'Felhaszn\xE1l\xF3 k\xF6vet\xE9se', | |
'ButtonSubscribed': 'K\xF6vet\xE9sek', | |
'ButtonSubscribedArtist': 'El\u0151ad\xF3 k\xF6vet\xE9se', | |
'ButtonSubscribedPlaylist': 'Lej\xE1tsz\xE1si lista k\xF6vet\xE9se', | |
'ButtonSubscribedUser': 'Felhaszn\xE1l\xF3 k\xF6vet\xE9se', | |
'ButtonUnsubscribe': 'Nem k\xF6vetem', | |
'ButtonUnsubscribeArtist': 'Ne k\xF6vesse az el\u0151ad\xF3t', | |
'ButtonUnsubscribePlaylist': 'Ne k\xF6vesse a lej\xE1tsz\xE1si list\xE1t', | |
'ButtonUnsubscribeUser': 'Ne k\xF6vesse a felhaszn\xE1l\xF3t', | |
'ButtonShare': 'Megoszt\xE1s\u2026', | |
'ButtonStartRadio': 'R\xE1di\xF3 ind\xEDt\xE1sa', | |
'PopupPlaylistSubscribeCancel': 'Nem', | |
'PopupPlaylistSubscribeConfirm': 'Ok\xE9', | |
'PopupPlaylistSubscribeLine1': 'Mivel k\xF6vetted ezt a lej\xE1tsz\xE1si list\xE1t, m\xE1r \u0151t is k\xF6veted: {0}.', | |
'PopupPlaylistSubscribeLine2': 'Folytatod, \xE9s k\xF6veted a felhaszn\xE1l\xF3t?', | |
'PopupPlaylistSuggestFollow': 'Ezt a lej\xE1tsz\xE1si list\xE1t {0} hozta l\xE9tre. Ha {1} t\xF6bb zen\xE9j\xE9t szeretn\xE9d {2}, csak nyomd meg a k\xF6vetkez\u0151t: {3}.', | |
'PopupAviciiAutoFollow': 'Mivel k\xF6vetted ezt a lej\xE1tsz\xE1si list\xE1t, most k\xF6veted Aviciit. K\xF6vesd Aviciit tov\xE1bbra is, ha \xE9rtes\xEDt\xE9seket szeretn\xE9l kapni az \xFAjdons\xE1gokr\xF3l.', | |
'DiscoverAppName': 'Fedezz fel', | |
'Followers': 'K\xF6vet\u0151k', | |
'SaveToYourMusic': 'Ment\xE9s', | |
'RemoveFromYourMusic': 'Elt\xE1vol\xEDt\xE1s', | |
'SavedToYourMusic': 'Mentve', | |
'User': 'Felhaszn\xE1l\xF3', | |
'Artist': 'El\u0151ad\xF3', | |
'Album': 'Album', | |
'Playlist': 'Lej\xE1tsz\xE1si lista' | |
}); | |
}, | |
'node_modules/views/id.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/id.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Ikuti', | |
'ButtonSubscribeArtist': 'Ikuti Artis', | |
'ButtonSubscribePlaylist': 'Ikuti Daftar Putar', | |
'ButtonSubscribeUser': 'Ikuti Pengguna', | |
'ButtonSubscribed': 'Mengikuti', | |
'ButtonSubscribedArtist': 'Mengikuti Artis', | |
'ButtonSubscribedPlaylist': 'Mengikuti Daftar Putar', | |
'ButtonSubscribedUser': 'Mengikuti Pengguna', | |
'ButtonUnsubscribe': 'Berhenti Mengikuti', | |
'ButtonUnsubscribeArtist': 'Berhenti Mengikuti Artis', | |
'ButtonUnsubscribePlaylist': 'Berhenti Mengikuti Daftar Putar', | |
'ButtonUnsubscribeUser': 'Berhenti Mengikuti Pengguna', | |
'ButtonShare': 'Bagikan\u2026', | |
'ButtonStartRadio': 'Mulai Radio', | |
'PopupPlaylistSubscribeCancel': 'Tidak', | |
'PopupPlaylistSubscribeConfirm': 'Oke', | |
'PopupPlaylistSubscribeLine1': 'Sekarang Anda juga mengikuti {0} karena Anda mengikuti daftar putar ini.', | |
'PopupPlaylistSubscribeLine2': 'Terus mengikuti pengguna ini?', | |
'PopupPlaylistSuggestFollow': '{0} membuat daftar putar ini. Untuk {2} musik lainnya dari {1}, cukup tekan {3}.', | |
'PopupAviciiAutoFollow': 'Anda mengikuti Avicii sekarang karena daftar putar ini Anda ikuti. Terus ikuti Avicii untuk memperoleh pemberitahuan tentang rilis mendatang!', | |
'DiscoverAppName': 'Temukan', | |
'Followers': 'Pengikut', | |
'SaveToYourMusic': 'Simpan', | |
'RemoveFromYourMusic': 'Hapus', | |
'SavedToYourMusic': 'Disimpan', | |
'User': 'Pengguna', | |
'Artist': 'Artis', | |
'Album': 'Album', | |
'Playlist': 'Daftar putar' | |
}); | |
}, | |
'node_modules/views/ja.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/ja.loc/strings/buttons.lang', { | |
'ButtonSubscribe': '\u30D5\u30A9\u30ED\u30FC', | |
'ButtonSubscribeArtist': '\u30A2\u30FC\u30C6\u30A3\u30B9\u30C8\u3092\u30D5\u30A9\u30ED\u30FC', | |
'ButtonSubscribePlaylist': '\u30D7\u30EC\u30A4\u30EA\u30B9\u30C8\u3092\u30D5\u30A9\u30ED\u30FC', | |
'ButtonSubscribeUser': '\u30D5\u30A9\u30ED\u30FC\u3059\u308B', | |
'ButtonSubscribed': '\u30D5\u30A9\u30ED\u30FC\u4E2D', | |
'ButtonSubscribedArtist': '\u30D5\u30A9\u30ED\u30FC\u3057\u3066\u3044\u308B\u30A2\u30FC\u30C6\u30A3\u30B9\u30C8', | |
'ButtonSubscribedPlaylist': '\u30D5\u30A9\u30ED\u30FC\u3057\u3066\u3044\u308B\u30D7\u30EC\u30A4\u30EA\u30B9\u30C8', | |
'ButtonSubscribedUser': '\u30D5\u30A9\u30ED\u30FC\u3057\u3066\u3044\u308B\u30E6\u30FC\u30B6\u30FC', | |
'ButtonUnsubscribe': '\u30D5\u30A9\u30ED\u30FC\u3092\u3084\u3081\u308B', | |
'ButtonUnsubscribeArtist': '\u30A2\u30FC\u30C6\u30A3\u30B9\u30C8\u306E\u30D5\u30A9\u30ED\u30FC\u3092\u3084\u3081\u308B', | |
'ButtonUnsubscribePlaylist': '\u30D7\u30EC\u30A4\u30EA\u30B9\u30C8\u306E\u306E\u30D5\u30A9\u30ED\u30FC\u3092\u3084\u3081\u308B', | |
'ButtonUnsubscribeUser': '\u30D5\u30A9\u30ED\u30FC\u3092\u3084\u3081\u308B', | |
'ButtonShare': '\u5171\u6709\u2026', | |
'ButtonStartRadio': '\u30E9\u30B8\u30AA\u3092\u958B\u59CB', | |
'PopupPlaylistSubscribeCancel': '\u3044\u3044\u3048', | |
'PopupPlaylistSubscribeConfirm': 'OK', | |
'PopupPlaylistSubscribeLine1': '\u3053\u306E\u30D7\u30EC\u30A4\u30EA\u30B9\u30C8\u3092\u30D5\u30A9\u30ED\u30FC\u3057\u305F\u306E\u3067\u3001{0}\u3082\u30D5\u30A9\u30ED\u30FC\u4E2D\u306B\u306A\u3063\u3066\u3044\u307E\u3059\u3002', | |
'PopupPlaylistSubscribeLine2': '\u3053\u306E\u30E6\u30FC\u30B6\u30FC\u306E\u30D5\u30A9\u30ED\u30FC\u3092\u7D9A\u3051\u307E\u3059\u304B?', | |
'PopupPlaylistSuggestFollow': '{0}\u304C\u3053\u306E\u30D7\u30EC\u30A4\u30EA\u30B9\u30C8\u3092\u4F5C\u6210\u3057\u307E\u3057\u305F\u3002{1}\u304B\u3089\u3082\u3063\u3068\u66F2\u3092{2}\u3059\u308B\u306B\u306F\u3001{3}\u3092\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u304F\u3060\u3055\u3044\u3002', | |
'PopupAviciiAutoFollow': 'Avicii\u306E\u30D7\u30EC\u30A4\u30EA\u30B9\u30C8\u3092\u30D5\u30A9\u30ED\u30FC\u3057\u3066\u3044\u308B\u306E\u3067\u3001\u73FE\u5728\u3082Avicii\u3092\u30D5\u30A9\u30ED\u30FC\u4E2D\u306B\u306A\u3063\u3066\u3044\u307E\u3059\u3002\u4ECA\u5F8C\u306E\u30EA\u30EA\u30FC\u30B9\u60C5\u5831\u77E5\u308B\u306B\u306F\u3001Avicii\u306E\u30D5\u30A9\u30ED\u30FC\u3092\u7D99\u7D9A\u3057\u307E\u3057\u3087\u3046\u3002', | |
'DiscoverAppName': '\u30C7\u30A3\u30B9\u30AB\u30D0\u30FC', | |
'Followers': '\u30D5\u30A9\u30ED\u30EF\u30FC', | |
'SaveToYourMusic': '\u4FDD\u5B58', | |
'RemoveFromYourMusic': '\u524A\u9664', | |
'SavedToYourMusic': '\u4FDD\u5B58\u6E08\u307F', | |
'User': '\u30E6\u30FC\u30B6\u30FC', | |
'Artist': '\u30A2\u30FC\u30C6\u30A3\u30B9\u30C8', | |
'Album': '\u30A2\u30EB\u30D0\u30E0', | |
'Playlist': '\u30D7\u30EC\u30A4\u30EA\u30B9\u30C8' | |
}); | |
}, | |
'node_modules/views/ko.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/ko.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Follow', | |
'ButtonSubscribeArtist': 'Follow Artist', | |
'ButtonSubscribePlaylist': 'Follow Playlist', | |
'ButtonSubscribeUser': 'Follow User', | |
'ButtonSubscribed': 'Following', | |
'ButtonSubscribedArtist': 'Following Artist', | |
'ButtonSubscribedPlaylist': 'Following Playlist', | |
'ButtonSubscribedUser': 'Following User', | |
'ButtonUnsubscribe': 'Unfollow', | |
'ButtonUnsubscribeArtist': 'Unfollow Artist', | |
'ButtonUnsubscribePlaylist': 'Unfollow Playlist', | |
'ButtonUnsubscribeUser': 'Unfollow User', | |
'ButtonShare': 'Share\u2026', | |
'ButtonStartRadio': 'Start Radio', | |
'PopupPlaylistSubscribeCancel': 'No', | |
'PopupPlaylistSubscribeConfirm': 'Okay', | |
'PopupPlaylistSubscribeLine1': 'You\'re now also following {0} because you followed this playlist.', | |
'PopupPlaylistSubscribeLine2': 'Continue to follow this user?', | |
'PopupPlaylistSuggestFollow': '{0} created this playlist. To {2} more music from {1}, just hit {3}.', | |
'PopupAviciiAutoFollow': 'You are now following Avicii because you followed this playlist. Keep following Avicii to get notified about future releases!', | |
'DiscoverAppName': 'Discover', | |
'Followers': 'Followers', | |
'SaveToYourMusic': 'Save', | |
'RemoveFromYourMusic': 'Remove', | |
'SavedToYourMusic': 'Saved', | |
'User': 'User', | |
'Artist': 'Artist', | |
'Album': 'Album', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/nl.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/nl.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Volgen', | |
'ButtonSubscribeArtist': 'Artiest volgen', | |
'ButtonSubscribePlaylist': 'Afspeellijst volgen', | |
'ButtonSubscribeUser': 'Gebruiker volgen', | |
'ButtonSubscribed': 'Volgend', | |
'ButtonSubscribedArtist': 'Volgt artiest', | |
'ButtonSubscribedPlaylist': 'Volgt afspeellijst', | |
'ButtonSubscribedUser': 'Volgt gebruiker', | |
'ButtonUnsubscribe': 'Ontvolgen', | |
'ButtonUnsubscribeArtist': 'Artiest niet meer volgen', | |
'ButtonUnsubscribePlaylist': 'Afspeellijst niet meer volgen', | |
'ButtonUnsubscribeUser': 'Gebruiker niet meer volgen', | |
'ButtonShare': 'Delen...', | |
'ButtonStartRadio': 'Radiozender beginnen', | |
'PopupPlaylistSubscribeCancel': 'Nee', | |
'PopupPlaylistSubscribeConfirm': 'OK', | |
'PopupPlaylistSubscribeLine1': 'Je volgt nu ook {0} omdat je deze afspeellijst volgt.', | |
'PopupPlaylistSubscribeLine2': 'Deze gebruiker blijven volgen?', | |
'PopupPlaylistSuggestFollow': '{0} heeft deze afspeellijst gemaakt. Als je meer muziek wilt {2} van {1}, selecteer je gewoon {3}.', | |
'PopupAviciiAutoFollow': 'Je volgt nu Avicii, omdat je deze afspeellijst hebt gevolgd. Blijf Avicii volgen om op de hoogte te blijven van toekomstige releases!', | |
'DiscoverAppName': 'Ontdekken', | |
'Followers': 'Volgers', | |
'SaveToYourMusic': 'Opslaan', | |
'RemoveFromYourMusic': 'Verwijderen', | |
'SavedToYourMusic': 'Opgeslagen', | |
'User': 'Gebruiker', | |
'Artist': 'Artiest', | |
'Album': 'Album', | |
'Playlist': 'Afspeellijst' | |
}); | |
}, | |
'node_modules/views/pl.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/pl.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Obserwuj', | |
'ButtonSubscribeArtist': 'Obserwuj wykonawc\u0119', | |
'ButtonSubscribePlaylist': 'Obserwuj playlist\u0119', | |
'ButtonSubscribeUser': 'Obserwuj u\u017Cytkownika', | |
'ButtonSubscribed': 'Obserwujesz', | |
'ButtonSubscribedArtist': 'Obserwuje wykonawc\u0119', | |
'ButtonSubscribedPlaylist': 'Obserwuje playlist\u0119', | |
'ButtonSubscribedUser': 'Obserwuje wykonawc\u0119', | |
'ButtonUnsubscribe': 'Nie obserwuj', | |
'ButtonUnsubscribeArtist': 'Nie obserwuj wykonawcy', | |
'ButtonUnsubscribePlaylist': 'Nie obserwuj playlisty', | |
'ButtonUnsubscribeUser': 'Nie obserwuj u\u017Cytkownika', | |
'ButtonShare': 'Udost\u0119pnij\u2026', | |
'ButtonStartRadio': 'W\u0142\u0105cz radio', | |
'PopupPlaylistSubscribeCancel': 'Nie', | |
'PopupPlaylistSubscribeConfirm': 'OK', | |
'PopupPlaylistSubscribeLine1': 'Poniewa\u017C obserwujesz t\u0119 playlist\u0119 teraz tak\u017Ce obserwujesz {0}.', | |
'PopupPlaylistSubscribeLine2': 'Czy nadal chcesz obserwowa\u0107 tego u\u017Cytkownika?', | |
'PopupPlaylistSuggestFollow': 'U\u017Cytkownik {0} utworzy\u0142 t\u0119 playlist\u0119. Aby {2} wi\u0119cej muzyki od {1}, po prostu kliknij {3}.', | |
'PopupAviciiAutoFollow': 'Obserwujesz teraz Avicii, poniewa\u017C playlista ta by\u0142a ju\u017C przez Ciebie wcze\u015Bniej obserwowana. Obserwuj nadal Avicii, aby otrzymywa\u0107 powiadomienia o przysz\u0142ych wydaniach!', | |
'DiscoverAppName': 'Odkrywaj', | |
'Followers': 'Obserwuj\u0105cych', | |
'SaveToYourMusic': 'Zapisz', | |
'RemoveFromYourMusic': 'Usu\u0144', | |
'SavedToYourMusic': 'Zapisane', | |
'User': 'U\u017Cytkownik', | |
'Artist': 'Wykonawca', | |
'Album': 'Album', | |
'Playlist': 'Playlista' | |
}); | |
}, | |
'node_modules/views/pt-br.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/pt-br.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Seguir', | |
'ButtonSubscribeArtist': 'Seguir artista', | |
'ButtonSubscribePlaylist': 'Seguir playlist', | |
'ButtonSubscribeUser': 'Seguir usu\xE1rio', | |
'ButtonSubscribed': 'Seguindo', | |
'ButtonSubscribedArtist': 'Seguindo artista', | |
'ButtonSubscribedPlaylist': 'Seguindo playlist', | |
'ButtonSubscribedUser': 'Seguindo usu\xE1rio', | |
'ButtonUnsubscribe': 'Deixar de seguir', | |
'ButtonUnsubscribeArtist': 'Deixar de seguir artista', | |
'ButtonUnsubscribePlaylist': 'Deixar de seguir playlist', | |
'ButtonUnsubscribeUser': 'Deixar de seguir usu\xE1rio', | |
'ButtonShare': 'Compartilhar\u2026', | |
'ButtonStartRadio': 'Iniciar R\xE1dio', | |
'PopupPlaylistSubscribeCancel': 'N\xE3o', | |
'PopupPlaylistSubscribeConfirm': 'OK', | |
'PopupPlaylistSubscribeLine1': 'Agora voc\xEA tamb\xE9m est\xE1 seguindo {0} porque seguiu esta playlist.', | |
'PopupPlaylistSubscribeLine2': 'Continuar a seguir esse usu\xE1rio?', | |
'PopupPlaylistSuggestFollow': '{0} criou esta playlist. Para {2} mais m\xFAsicas de {1}, basta apertar {3}.', | |
'PopupAviciiAutoFollow': 'Agora voc\xEA est\xE1 seguindo Avicii, porque seguiu esta playlist. Continue seguindo Avicii para receber notifica\xE7\xF5es sobre lan\xE7amentos futuros!', | |
'DiscoverAppName': 'Descobrir', | |
'Followers': 'Seguidores', | |
'SaveToYourMusic': 'Salvar', | |
'RemoveFromYourMusic': 'Remover', | |
'SavedToYourMusic': 'Salvo', | |
'User': 'Usu\xE1rio', | |
'Artist': 'Artista', | |
'Album': '\xC1lbum', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/it.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/it.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Segui', | |
'ButtonSubscribeArtist': 'Segui artista', | |
'ButtonSubscribePlaylist': 'Segui playlist', | |
'ButtonSubscribeUser': 'Segui utente', | |
'ButtonSubscribed': 'Following', | |
'ButtonSubscribedArtist': 'Artista che segui', | |
'ButtonSubscribedPlaylist': 'Playlist che segui', | |
'ButtonSubscribedUser': 'Utente che segui', | |
'ButtonUnsubscribe': 'Non seguire', | |
'ButtonUnsubscribeArtist': 'Smetti di seguire l\'artista', | |
'ButtonUnsubscribePlaylist': 'Smetti di seguire la playlist', | |
'ButtonUnsubscribeUser': 'Smetti di seguire l\'utente', | |
'ButtonShare': 'Condividi\u2026', | |
'ButtonStartRadio': 'Avvia radio', | |
'PopupPlaylistSubscribeCancel': 'No', | |
'PopupPlaylistSubscribeConfirm': 'Ok', | |
'PopupPlaylistSubscribeLine1': 'Ora segui anche {0} in quanto seguivi questa playlist.', | |
'PopupPlaylistSubscribeLine2': 'Vuoi continuare a seguire questo utente?', | |
'PopupPlaylistSuggestFollow': '{0} ha creato questa playlist. {2} pi\xF9 musica da {1}, premendo {3}.', | |
'PopupAviciiAutoFollow': 'Ora segui Avicii in quanto seguivi questa playlist. Continua a seguire Avicii per ricevere notifiche sulle sue uscite future.', | |
'DiscoverAppName': 'Scopri', | |
'Followers': 'Follower', | |
'SaveToYourMusic': 'Salva', | |
'RemoveFromYourMusic': 'Elimina', | |
'SavedToYourMusic': 'Salvato', | |
'User': 'Utente', | |
'Artist': 'Artista', | |
'Album': 'Album', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/ro.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/ro.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Urm\u0103re\u015Fte', | |
'ButtonSubscribeArtist': 'Urm\u0103re\u015Fte artistul', | |
'ButtonSubscribePlaylist': 'Urm\u0103re\u015Fte playlistul', | |
'ButtonSubscribeUser': 'Urm\u0103re\u015Fte utilizatorul', | |
'ButtonSubscribed': 'Urm\u0103resc', | |
'ButtonSubscribedArtist': 'Se urm\u0103re\u015Fte artistul', | |
'ButtonSubscribedPlaylist': 'Se urm\u0103re\u015Fte playlistul', | |
'ButtonSubscribedUser': 'Urm\u0103resc utilizatorul', | |
'ButtonUnsubscribe': 'Opre\u015Fte urm\u0103rirea', | |
'ButtonUnsubscribeArtist': 'Opre\u015Fte urm\u0103rirea artistului', | |
'ButtonUnsubscribePlaylist': 'Opre\u015Fte urm\u0103rirea playlistului', | |
'ButtonUnsubscribeUser': 'Opre\u015Fte urm\u0103rirea utilizatorului', | |
'ButtonShare': 'Partajeaz\u0103...', | |
'ButtonStartRadio': 'Creeaz\u0103 un post de radio', | |
'PopupPlaylistSubscribeCancel': 'Nu', | |
'PopupPlaylistSubscribeConfirm': 'Ok', | |
'PopupPlaylistSubscribeLine1': 'Acum urm\u0103re\u015Fti \u015Fi {0} deoarece ai urm\u0103rit acest playlist.', | |
'PopupPlaylistSubscribeLine2': 'Vrei s\u0103 continui s\u0103 urm\u0103re\u015Fti acest utilizator?', | |
'PopupPlaylistSuggestFollow': '{0} a creat acest playlist. Pentru a {2} mai mult\u0103 muzic\u0103 de la {1}, apas\u0103 {3}.', | |
'PopupAviciiAutoFollow': 'Acum urm\u0103re\u015Fti Avicii deoarece ai urm\u0103rit acest playlist. Continu\u0103 s\u0103 urm\u0103re\u015Fti Avicii pentru a primi notific\u0103ri despre lans\u0103rile viitoare!', | |
'DiscoverAppName': 'Descoper\u0103', | |
'Followers': 'Persoane care urm\u0103resc', | |
'SaveToYourMusic': 'Salveaz\u0103', | |
'RemoveFromYourMusic': 'Elimin\u0103', | |
'SavedToYourMusic': 'Salvat', | |
'User': 'Utilizator', | |
'Artist': 'Artist', | |
'Album': 'Album', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/ru.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/ru.loc/strings/buttons.lang', { | |
'ButtonSubscribe': '\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F', | |
'ButtonSubscribeArtist': '\u0421\u043B\u0435\u0434\u0438\u0442\u044C \u0437\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F\u043C\u0438', | |
'ButtonSubscribePlaylist': '\u0421\u043B\u0435\u0434\u0438\u0442\u044C \u0437\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F\u043C\u0438', | |
'ButtonSubscribeUser': '\u0421\u043B\u0435\u0434\u0438\u0442\u044C \u0437\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F\u043C\u0438', | |
'ButtonSubscribed': '\u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0438', | |
'ButtonSubscribedArtist': '\u0412\u044B \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043D\u044B \u043D\u0430 \u044D\u0442\u043E\u0433\u043E \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F', | |
'ButtonSubscribedPlaylist': '\u0412\u044B \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043D\u044B \u043D\u0430 \u044D\u0442\u043E\u0442 \u043F\u043B\u0435\u0439\u043B\u0438\u0441\u0442', | |
'ButtonSubscribedUser': '\u0412\u044B \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043D\u044B \u043D\u0430 \u044D\u0442\u043E\u0433\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F', | |
'ButtonUnsubscribe': '\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0443', | |
'ButtonUnsubscribeArtist': '\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0443 \u043D\u0430 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F', | |
'ButtonUnsubscribePlaylist': '\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0443 \u043D\u0430 \u043F\u043B\u0435\u0439\u043B\u0438\u0441\u0442', | |
'ButtonUnsubscribeUser': '\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0443 \u043D\u0430 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F', | |
'ButtonShare': '\u041F\u043E\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F\u2026', | |
'ButtonStartRadio': '\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0440\u0430\u0434\u0438\u043E', | |
'PopupPlaylistSubscribeCancel': '\u041D\u0435\u0442', | |
'PopupPlaylistSubscribeConfirm': '\u041E\u041A', | |
'PopupPlaylistSubscribeLine1': '\u0422\u0435\u043F\u0435\u0440\u044C \u0432\u044B \u0441\u043B\u0435\u0434\u0438\u0442\u0435 \u0437\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F\u043C\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F {0}, \u0442\u0430\u043A \u043A\u0430\u043A \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043B\u0438\u0441\u044C \u043D\u0430 \u0435\u0433\u043E \u043F\u043B\u0435\u0439\u043B\u0438\u0441\u0442.', | |
'PopupPlaylistSubscribeLine2': '\u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C \u0441\u043B\u0435\u0434\u0438\u0442\u044C \u0437\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F\u043C\u0438 \u044D\u0442\u043E\u0433\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F?', | |
'PopupPlaylistSuggestFollow': '\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C {0} \u0441\u043E\u0437\u0434\u0430\u043B \u044D\u0442\u043E\u0442 \u043F\u043B\u0435\u0439\u043B\u0438\u0441\u0442. \u0427\u0442\u043E\u0431\u044B \u0435\u0449\u0435 {2} \u043C\u0443\u0437\u044B\u043A\u0443 {1}, \u043F\u0440\u043E\u0441\u0442\u043E \u043D\u0430\u0436\u043C\u0438\u0442\u0435 {3}.', | |
'PopupAviciiAutoFollow': '\u0422\u0435\u043F\u0435\u0440\u044C \u0432\u044B \u0441\u043B\u0435\u0434\u0438\u0442\u0435 \u0437\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F\u043C\u0438 \u0410\u0432\u0438\u0447\u0438, \u0442\u0430\u043A \u043A\u0430\u043A \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043B\u0438\u0441\u044C \u043D\u0430 \u0435\u0433\u043E \u043F\u043B\u0435\u0439\u043B\u0438\u0441\u0442. \u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0439\u0442\u0435 \u0441\u043B\u0435\u0434\u0438\u0442\u044C \u0437\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F\u043C\u0438, \u0447\u0442\u043E\u0431\u044B \u043D\u0435 \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u043D\u043E\u0432\u044B\u0435 \u0440\u0435\u043B\u0438\u0437\u044B.', | |
'DiscoverAppName': '\u041F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u044F', | |
'Followers': '\u041F\u043E\u0434\u043F\u0438\u0441\u0447\u0438\u043A\u0438', | |
'SaveToYourMusic': '\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C', | |
'RemoveFromYourMusic': '\u0423\u0434\u0430\u043B\u0438\u0442\u044C', | |
'SavedToYourMusic': '\u0421\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043E', | |
'User': '\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C', | |
'Artist': '\u0418\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C', | |
'Album': '\u0410\u043B\u044C\u0431\u043E\u043C', | |
'Playlist': '\u041F\u043B\u0435\u0439\u043B\u0438\u0441\u0442' | |
}); | |
}, | |
'node_modules/views/ta.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/ta.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Follow', | |
'ButtonSubscribeArtist': 'Follow Artist', | |
'ButtonSubscribePlaylist': 'Follow Playlist', | |
'ButtonSubscribeUser': 'Follow User', | |
'ButtonSubscribed': 'Following', | |
'ButtonSubscribedArtist': 'Following Artist', | |
'ButtonSubscribedPlaylist': 'Following Playlist', | |
'ButtonSubscribedUser': 'Following User', | |
'ButtonUnsubscribe': 'Unfollow', | |
'ButtonUnsubscribeArtist': 'Unfollow Artist', | |
'ButtonUnsubscribePlaylist': 'Unfollow Playlist', | |
'ButtonUnsubscribeUser': 'Unfollow User', | |
'ButtonShare': 'Share\u2026', | |
'ButtonStartRadio': 'Start Radio', | |
'PopupPlaylistSubscribeCancel': 'No', | |
'PopupPlaylistSubscribeConfirm': 'Okay', | |
'PopupPlaylistSubscribeLine1': 'You\'re now also following {0} because you followed this playlist.', | |
'PopupPlaylistSubscribeLine2': 'Continue to follow this user?', | |
'PopupPlaylistSuggestFollow': '{0} created this playlist. To {2} more music from {1}, just hit {3}.', | |
'PopupAviciiAutoFollow': 'You are now following Avicii because you followed this playlist. Keep following Avicii to get notified about future releases!', | |
'DiscoverAppName': 'Discover', | |
'Followers': 'Followers', | |
'SaveToYourMusic': 'Save', | |
'RemoveFromYourMusic': 'Remove', | |
'SavedToYourMusic': 'Saved', | |
'User': 'User', | |
'Artist': 'Artist', | |
'Album': 'Album', | |
'Playlist': 'Playlist' | |
}); | |
}, | |
'node_modules/views/th.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/th.loc/strings/buttons.lang', { | |
'ButtonSubscribe': '\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21', | |
'ButtonSubscribeArtist': '\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21\u0E28\u0E34\u0E25\u0E1B\u0E34\u0E19', | |
'ButtonSubscribePlaylist': '\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21\u0E40\u0E1E\u0E25\u0E22\u0E4C\u0E25\u0E34\u0E2A\u0E15\u0E4C', | |
'ButtonSubscribeUser': '\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21\u0E1C\u0E39\u0E49\u0E43\u0E0A\u0E49', | |
'ButtonSubscribed': '\u0E01\u0E33\u0E25\u0E31\u0E07\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21', | |
'ButtonSubscribedArtist': '\u0E01\u0E33\u0E25\u0E31\u0E07\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21\u0E28\u0E34\u0E25\u0E1B\u0E34\u0E19', | |
'ButtonSubscribedPlaylist': '\u0E01\u0E33\u0E25\u0E31\u0E07\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21\u0E40\u0E1E\u0E25\u0E22\u0E4C\u0E25\u0E34\u0E2A\u0E15\u0E4C', | |
'ButtonSubscribedUser': '\u0E01\u0E33\u0E25\u0E31\u0E07\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21\u0E1C\u0E39\u0E49\u0E43\u0E0A\u0E49', | |
'ButtonUnsubscribe': '\u0E40\u0E25\u0E34\u0E01\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21', | |
'ButtonUnsubscribeArtist': '\u0E40\u0E25\u0E34\u0E01\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21\u0E28\u0E34\u0E25\u0E1B\u0E34\u0E19', | |
'ButtonUnsubscribePlaylist': '\u0E40\u0E25\u0E34\u0E01\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21\u0E40\u0E1E\u0E25\u0E22\u0E4C\u0E25\u0E34\u0E2A\u0E15\u0E4C', | |
'ButtonUnsubscribeUser': '\u0E40\u0E25\u0E34\u0E01\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21\u0E1C\u0E39\u0E49\u0E43\u0E0A\u0E49', | |
'ButtonShare': '\u0E41\u0E0A\u0E23\u0E4C\u2026', | |
'ButtonStartRadio': '\u0E40\u0E23\u0E34\u0E48\u0E21\u0E43\u0E0A\u0E49\u0E27\u0E34\u0E17\u0E22\u0E38', | |
'PopupPlaylistSubscribeCancel': '\u0E44\u0E21\u0E48', | |
'PopupPlaylistSubscribeConfirm': '\u0E15\u0E01\u0E25\u0E07', | |
'PopupPlaylistSubscribeLine1': '\u0E04\u0E38\u0E13\u0E01\u0E33\u0E25\u0E31\u0E07\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21 {0} \u0E14\u0E49\u0E27\u0E22\u0E40\u0E19\u0E37\u0E48\u0E2D\u0E07\u0E08\u0E32\u0E01\u0E04\u0E38\u0E13\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21\u0E40\u0E1E\u0E25\u0E22\u0E4C\u0E25\u0E34\u0E2A\u0E15\u0E4C\u0E19\u0E35\u0E49', | |
'PopupPlaylistSubscribeLine2': '\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21\u0E1C\u0E39\u0E49\u0E43\u0E0A\u0E49\u0E19\u0E35\u0E49\u0E15\u0E48\u0E2D\u0E44\u0E1B\u0E2B\u0E23\u0E37\u0E2D\u0E44\u0E21\u0E48', | |
'PopupPlaylistSuggestFollow': '{0} \u0E2A\u0E23\u0E49\u0E32\u0E07\u0E40\u0E1E\u0E25\u0E22\u0E4C\u0E25\u0E34\u0E2A\u0E15\u0E4C\u0E19\u0E35\u0E49 \u0E2D\u0E22\u0E32\u0E01 {2} \u0E40\u0E1E\u0E25\u0E07\u0E21\u0E32\u0E01\u0E01\u0E27\u0E48\u0E32\u0E19\u0E35\u0E49\u0E08\u0E32\u0E01 {1} \u0E40\u0E1E\u0E35\u0E22\u0E07\u0E41\u0E04\u0E48\u0E04\u0E25\u0E34\u0E01 {3}', | |
'PopupAviciiAutoFollow': '\u0E02\u0E13\u0E30\u0E19\u0E35\u0E49\u0E04\u0E38\u0E13\u0E40\u0E23\u0E34\u0E48\u0E21\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21 Avicii \u0E40\u0E19\u0E37\u0E48\u0E2D\u0E07\u0E08\u0E32\u0E01\u0E04\u0E38\u0E13\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21\u0E40\u0E1E\u0E25\u0E22\u0E4C\u0E25\u0E34\u0E2A\u0E15\u0E4C\u0E19\u0E35\u0E49 \u0E15\u0E34\u0E14\u0E15\u0E32\u0E21 Avicii \u0E15\u0E48\u0E2D\u0E44\u0E1B\u0E40\u0E1E\u0E37\u0E48\u0E2D\u0E23\u0E31\u0E1A\u0E01\u0E32\u0E23\u0E41\u0E08\u0E49\u0E07\u0E40\u0E15\u0E37\u0E2D\u0E19\u0E40\u0E01\u0E35\u0E48\u0E22\u0E27\u0E01\u0E31\u0E1A\u0E1C\u0E25\u0E07\u0E32\u0E19\u0E43\u0E2B\u0E21\u0E48\u0E46 \u0E43\u0E19\u0E2D\u0E19\u0E32\u0E04\u0E15!', | |
'DiscoverAppName': 'Discover', | |
'Followers': '\u0E1C\u0E39\u0E49\u0E15\u0E34\u0E14\u0E15\u0E32\u0E21', | |
'SaveToYourMusic': '\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01', | |
'RemoveFromYourMusic': '\u0E19\u0E33\u0E2D\u0E2D\u0E01', | |
'SavedToYourMusic': '\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E41\u0E25\u0E49\u0E27', | |
'User': '\u0E1C\u0E39\u0E49\u0E43\u0E0A\u0E49', | |
'Artist': '\u0E28\u0E34\u0E25\u0E1B\u0E34\u0E19', | |
'Album': '\u0E2D\u0E31\u0E25\u0E1A\u0E31\u0E49\u0E21', | |
'Playlist': '\u0E40\u0E1E\u0E25\u0E22\u0E4C\u0E25\u0E34\u0E2A\u0E15\u0E4C' | |
}); | |
}, | |
'node_modules/views/tr.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/tr.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Takip Et', | |
'ButtonSubscribeArtist': 'Sanat\xE7\u0131y\u0131 Takip Et', | |
'ButtonSubscribePlaylist': '\xC7alma Listesini Takip Et', | |
'ButtonSubscribeUser': 'Kullan\u0131c\u0131y\u0131 Takip Et', | |
'ButtonSubscribed': 'Takip Ediliyor', | |
'ButtonSubscribedArtist': 'Sanat\xE7\u0131 Takip Ediliyor', | |
'ButtonSubscribedPlaylist': '\xC7alma Listesi Takip Ediliyor', | |
'ButtonSubscribedUser': 'Kullan\u0131c\u0131 Takip Ediliyor', | |
'ButtonUnsubscribe': 'Takip Etmeyi B\u0131rak', | |
'ButtonUnsubscribeArtist': 'Sanat\xE7\u0131y\u0131 Takip Etmeyi B\u0131rak', | |
'ButtonUnsubscribePlaylist': '\xC7alma Listesini Takip Etmeyi B\u0131rak', | |
'ButtonUnsubscribeUser': 'Kullan\u0131c\u0131y\u0131 Takip Etmeyi B\u0131rak', | |
'ButtonShare': 'Payla\u015F...', | |
'ButtonStartRadio': 'Radyo\'yu Ba\u015Flat', | |
'PopupPlaylistSubscribeCancel': 'Hay\u0131r', | |
'PopupPlaylistSubscribeConfirm': 'Tamam', | |
'PopupPlaylistSubscribeLine1': 'Bu \xE7alma listesini takip etti\u011Fin i\xE7in {0} adl\u0131 ki\u015Fiyi de takip ediyorsun.', | |
'PopupPlaylistSubscribeLine2': 'Bu kullan\u0131c\u0131y\u0131 takip etmeye devam edecek misin?', | |
'PopupPlaylistSuggestFollow': '{0} bu \xE7alma listesini olu\u015Fturdu. {3} d\xFC\u011Fmesine bas ve {1} adl\u0131 ki\u015Fiden daha fazla m\xFCzik {2}.', | |
'PopupAviciiAutoFollow': '\u015Eu anda Avicii\'yi takip ediyorsun \xE7\xFCnk\xFC bu \xE7alma listesini takip ettin. Gelecek par\xE7alar i\xE7in Avicii\'yi takip etmeye devam et!', | |
'DiscoverAppName': 'Ke\u015Ffet', | |
'Followers': 'Takip\xE7iler', | |
'SaveToYourMusic': 'Kaydet', | |
'RemoveFromYourMusic': '\xC7\u0131kar', | |
'SavedToYourMusic': 'Kaydedildi', | |
'User': 'Kullan\u0131c\u0131', | |
'Artist': 'Sanat\xE7\u0131', | |
'Album': 'Alb\xFCm', | |
'Playlist': '\xC7alma Listesi' | |
}); | |
}, | |
'node_modules/views/zh-hant.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/zh-hant.loc/strings/buttons.lang', { | |
'ButtonSubscribe': '\u95DC\u6CE8', | |
'ButtonSubscribeArtist': '\u95DC\u6CE8\u85DD\u4EBA', | |
'ButtonSubscribePlaylist': '\u95DC\u6CE8\u64AD\u653E\u6E05\u55AE', | |
'ButtonSubscribeUser': '\u95DC\u6CE8\u4F7F\u7528\u8005', | |
'ButtonSubscribed': '\u6B63\u5728\u95DC\u6CE8', | |
'ButtonSubscribedArtist': '\u95DC\u6CE8\u85DD\u4EBA', | |
'ButtonSubscribedPlaylist': '\u95DC\u6CE8\u64AD\u653E\u6E05\u55AE', | |
'ButtonSubscribedUser': '\u95DC\u6CE8\u4F7F\u7528\u8005', | |
'ButtonUnsubscribe': '\u53D6\u6D88\u95DC\u6CE8', | |
'ButtonUnsubscribeArtist': '\u53D6\u6D88\u95DC\u6CE8\u85DD\u4EBA', | |
'ButtonUnsubscribePlaylist': '\u53D6\u6D88\u95DC\u6CE8\u64AD\u653E\u6E05\u55AE', | |
'ButtonUnsubscribeUser': '\u53D6\u6D88\u95DC\u6CE8\u4F7F\u7528\u8005', | |
'ButtonShare': '\u5206\u4EAB...', | |
'ButtonStartRadio': '\u958B\u59CB\u6536\u807D\u96FB\u53F0', | |
'PopupPlaylistSubscribeCancel': '\u5426', | |
'PopupPlaylistSubscribeConfirm': '\u78BA\u5B9A', | |
'PopupPlaylistSubscribeLine1': '\u7531\u65BC\u4F60\u95DC\u6CE8\u9019\u500B\u64AD\u653E\u6E05\u55AE\uFF0C\u6240\u4EE5\u4E5F\u540C\u6642\u95DC\u6CE8 {0}\u3002', | |
'PopupPlaylistSubscribeLine2': '\u7E7C\u7E8C\u95DC\u6CE8\u9019\u4F4D\u4F7F\u7528\u8005\u55CE\uFF1F', | |
'PopupPlaylistSuggestFollow': '{0} \u5EFA\u7ACB\u4E86\u9019\u500B\u64AD\u653E\u6E05\u55AE\u3002 \u60F3{2}\u66F4\u591A\u4F86\u81EA {1} \u7684\u97F3\u6A02\uFF0C\u8ACB\u9EDE\u9078{3}\u3002', | |
'PopupAviciiAutoFollow': '\u7531\u65BC\u4F60\u95DC\u6CE8\u4E86 Avicii \u7684\u64AD\u653E\u6E05\u55AE\uFF0C\u8868\u793A\u4F60\u4E5F\u6B63\u5728\u95DC\u6CE8\u4ED6\u3002\u7E7C\u7E8C\u95DC\u6CE8 Avicii\uFF0C\u638C\u63E1\u65B0\u6B4C\u767C\u884C\u6D88\u606F\uFF01', | |
'DiscoverAppName': '\u767C\u6398', | |
'Followers': '\u7C89\u7D72', | |
'SaveToYourMusic': '\u5132\u5B58', | |
'RemoveFromYourMusic': '\u79FB\u9664', | |
'SavedToYourMusic': '\u5DF2\u5132\u5B58', | |
'User': '\u4F7F\u7528\u8005', | |
'Artist': '\u85DD\u4EBA', | |
'Album': '\u5C08\u8F2F', | |
'Playlist': '\u64AD\u653E\u6E05\u55AE' | |
}); | |
}, | |
'node_modules/views/zsm.loc/strings/buttons.lang': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = new SpotifyApi.LangModule('node_modules/views/zsm.loc/strings/buttons.lang', { | |
'ButtonSubscribe': 'Ikuti', | |
'ButtonSubscribeArtist': 'Ikuti Artis', | |
'ButtonSubscribePlaylist': 'Ikuti Senarai main', | |
'ButtonSubscribeUser': 'Ikuti Pengguna', | |
'ButtonSubscribed': 'Mengikuti', | |
'ButtonSubscribedArtist': 'Mengikuti artis', | |
'ButtonSubscribedPlaylist': 'Mengikuti Senarai main', | |
'ButtonSubscribedUser': 'Mengikuti Pengguna', | |
'ButtonUnsubscribe': 'Nyahikut', | |
'ButtonUnsubscribeArtist': 'Nyahikut Artis', | |
'ButtonUnsubscribePlaylist': 'Nyahikut Senarai main', | |
'ButtonUnsubscribeUser': 'Nyahikut Pengguna', | |
'ButtonShare': 'Kongsi\u2026', | |
'ButtonStartRadio': 'Mulakan Radio', | |
'PopupPlaylistSubscribeCancel': 'Tidak', | |
'PopupPlaylistSubscribeConfirm': 'Ok', | |
'PopupPlaylistSubscribeLine1': 'Anda kini mengikuti {0} kerana anda mengikuti senarai main ini.', | |
'PopupPlaylistSubscribeLine2': 'Teruskan untuk mengikuti pengguna ini?', | |
'PopupPlaylistSuggestFollow': '{0} mencipta senarai main ini. Untuk {2} lebih banyak muzik daripada {1}, hanya tekan {3}.', | |
'PopupAviciiAutoFollow': 'Anda kini mengikuti Avicii kerana anda mengikuti senarai main ini. Pastikan mengikuti Avicii untuk mendapat pemberitahuan tentang keluaran di masa depan!', | |
'DiscoverAppName': 'Temui', | |
'Followers': 'Pengikut', | |
'SaveToYourMusic': 'Simpan', | |
'RemoveFromYourMusic': 'Keluarkan', | |
'SavedToYourMusic': 'Disimpan', | |
'User': 'Pengguna', | |
'Artist': 'Artis', | |
'Album': 'Album', | |
'Playlist': 'Senarai main' | |
}); | |
}, | |
'node_modules/mout/object/hasOwn.js': function (require, module, exports, global, __filename, __dirname) { | |
function hasOwn(obj, prop) { | |
return Object.prototype.hasOwnProperty.call(obj, prop); | |
} | |
module.exports = hasOwn; | |
}, | |
'node_modules/mout/object/forIn.js': function (require, module, exports, global, __filename, __dirname) { | |
var hasOwn = require('node_modules/mout/object/hasOwn.js'); | |
var _hasDontEnumBug, _dontEnums; | |
function checkDontEnum() { | |
_dontEnums = [ | |
'toString', | |
'toLocaleString', | |
'valueOf', | |
'hasOwnProperty', | |
'isPrototypeOf', | |
'propertyIsEnumerable', | |
'constructor' | |
]; | |
_hasDontEnumBug = true; | |
for (var key in { 'toString': null }) { | |
_hasDontEnumBug = false; | |
} | |
} | |
function forIn(obj, fn, thisObj) { | |
var key, i = 0; | |
if (_hasDontEnumBug == null) | |
checkDontEnum(); | |
for (key in obj) { | |
if (exec(fn, obj, key, thisObj) === false) { | |
break; | |
} | |
} | |
if (_hasDontEnumBug) { | |
var ctor = obj.constructor, isProto = !!ctor && obj === ctor.prototype; | |
while (key = _dontEnums[i++]) { | |
if ((key !== 'constructor' || !isProto && hasOwn(obj, key)) && obj[key] !== Object.prototype[key]) { | |
if (exec(fn, obj, key, thisObj) === false) { | |
break; | |
} | |
} | |
} | |
} | |
} | |
function exec(fn, obj, key, thisObj) { | |
return fn.call(thisObj, obj[key], key, obj); | |
} | |
module.exports = forIn; | |
}, | |
'node_modules/mout/object/forOwn.js': function (require, module, exports, global, __filename, __dirname) { | |
var hasOwn = require('node_modules/mout/object/hasOwn.js'); | |
var forIn = require('node_modules/mout/object/forIn.js'); | |
function forOwn(obj, fn, thisObj) { | |
forIn(obj, function (val, key) { | |
if (hasOwn(obj, key)) { | |
return fn.call(thisObj, obj[key], key, obj); | |
} | |
}); | |
} | |
module.exports = forOwn; | |
}, | |
'node_modules/mout/object/mixIn.js': function (require, module, exports, global, __filename, __dirname) { | |
var forOwn = require('node_modules/mout/object/forOwn.js'); | |
function mixIn(target, objects) { | |
var i = 0, n = arguments.length, obj; | |
while (++i < n) { | |
obj = arguments[i]; | |
if (obj != null) { | |
forOwn(obj, copyProp, target); | |
} | |
} | |
return target; | |
} | |
function copyProp(val, key) { | |
this[key] = val; | |
} | |
module.exports = mixIn; | |
}, | |
'node_modules/mout/array/indexOf.js': function (require, module, exports, global, __filename, __dirname) { | |
function indexOf(arr, item, fromIndex) { | |
fromIndex = fromIndex || 0; | |
if (arr == null) { | |
return -1; | |
} | |
var len = arr.length, i = fromIndex < 0 ? len + fromIndex : fromIndex; | |
while (i < len) { | |
if (arr[i] === item) { | |
return i; | |
} | |
i++; | |
} | |
return -1; | |
} | |
module.exports = indexOf; | |
}, | |
'node_modules/mout/array/contains.js': function (require, module, exports, global, __filename, __dirname) { | |
var indexOf = require('node_modules/mout/array/indexOf.js'); | |
function contains(arr, val) { | |
return indexOf(arr, val) !== -1; | |
} | |
module.exports = contains; | |
}, | |
'node_modules/mout/array/combine.js': function (require, module, exports, global, __filename, __dirname) { | |
var indexOf = require('node_modules/mout/array/indexOf.js'); | |
function combine(arr1, arr2) { | |
if (arr2 == null) { | |
return arr1; | |
} | |
var i = -1, len = arr2.length; | |
while (++i < len) { | |
if (indexOf(arr1, arr2[i]) === -1) { | |
arr1.push(arr2[i]); | |
} | |
} | |
return arr1; | |
} | |
module.exports = combine; | |
}, | |
'node_modules/mout/array/remove.js': function (require, module, exports, global, __filename, __dirname) { | |
var indexOf = require('node_modules/mout/array/indexOf.js'); | |
function remove(arr, item) { | |
var idx = indexOf(arr, item); | |
if (idx !== -1) | |
arr.splice(idx, 1); | |
} | |
module.exports = remove; | |
}, | |
'node_modules/mout/lang/createObject.js': function (require, module, exports, global, __filename, __dirname) { | |
var mixIn = require('node_modules/mout/object/mixIn.js'); | |
function createObject(parent, props) { | |
function F() { | |
} | |
F.prototype = parent; | |
return mixIn(new F(), props); | |
} | |
module.exports = createObject; | |
}, | |
'node_modules/prime/index.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var hasOwn = require('node_modules/mout/object/hasOwn.js'), mixIn = require('node_modules/mout/object/mixIn.js'), create = require('node_modules/mout/lang/createObject.js'), kindOf = require('node_modules/mout/lang/kindOf.js'); | |
var hasDescriptors = true; | |
try { | |
Object.defineProperty({}, '~', {}); | |
Object.getOwnPropertyDescriptor({}, '~'); | |
} catch (e) { | |
hasDescriptors = false; | |
} | |
var hasEnumBug = !{ valueOf: 0 }.propertyIsEnumerable('valueOf'), buggy = [ | |
'toString', | |
'valueOf' | |
]; | |
var verbs = /^constructor|inherits|mixin$/; | |
var implement = function (proto) { | |
var prototype = this.prototype; | |
for (var key in proto) { | |
if (key.match(verbs)) | |
continue; | |
if (hasDescriptors) { | |
var descriptor = Object.getOwnPropertyDescriptor(proto, key); | |
if (descriptor) { | |
Object.defineProperty(prototype, key, descriptor); | |
continue; | |
} | |
} | |
prototype[key] = proto[key]; | |
} | |
if (hasEnumBug) | |
for (var i = 0; key = buggy[i]; i++) { | |
var value = proto[key]; | |
if (value !== Object.prototype[key]) | |
prototype[key] = value; | |
} | |
return this; | |
}; | |
var prime = function (proto) { | |
if (kindOf(proto) === 'Function') | |
proto = { constructor: proto }; | |
var superprime = proto.inherits; | |
var constructor = hasOwn(proto, 'constructor') ? proto.constructor : superprime ? function () { | |
return superprime.apply(this, arguments); | |
} : function () { | |
}; | |
if (superprime) { | |
mixIn(constructor, superprime); | |
var superproto = superprime.prototype; | |
var cproto = constructor.prototype = create(superproto); | |
constructor.parent = superproto; | |
cproto.constructor = constructor; | |
} | |
if (!constructor.implement) | |
constructor.implement = implement; | |
var mixins = proto.mixin; | |
if (mixins) { | |
if (kindOf(mixins) !== 'Array') | |
mixins = [mixins]; | |
for (var i = 0; i < mixins.length; i++) | |
constructor.implement(create(mixins[i].prototype)); | |
} | |
return constructor.implement(proto); | |
}; | |
module.exports = prime; | |
}, | |
'node_modules/mout/array/reduce.js': function (require, module, exports, global, __filename, __dirname) { | |
function reduce(arr, fn, initVal) { | |
var hasInit = arguments.length > 2, result = initVal; | |
if (arr == null || !arr.length) { | |
if (!hasInit) { | |
throw new Error('reduce of empty array with no initial value'); | |
} else { | |
return initVal; | |
} | |
} | |
var i = -1, len = arr.length; | |
while (++i < len) { | |
if (!hasInit) { | |
result = arr[i]; | |
hasInit = true; | |
} else { | |
result = fn(result, arr[i], i, arr); | |
} | |
} | |
return result; | |
} | |
module.exports = reduce; | |
}, | |
'node_modules/mout/time/now.js': function (require, module, exports, global, __filename, __dirname) { | |
function now() { | |
return now.get(); | |
} | |
now.get = typeof Date.now === 'function' ? Date.now : function () { | |
return +new Date(); | |
}; | |
module.exports = now; | |
}, | |
'node_modules/prime/defer.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var kindOf = require('node_modules/mout/lang/kindOf.js'), now = require('node_modules/mout/time/now.js'), forEach = require('node_modules/mout/array/forEach.js'), indexOf = require('node_modules/mout/array/indexOf.js'); | |
var callbacks = { | |
timeout: {}, | |
frame: [], | |
immediate: [] | |
}; | |
var push = function (collection, callback, context, defer) { | |
var iterator = function () { | |
iterate(collection); | |
}; | |
if (!collection.length) | |
defer(iterator); | |
var entry = { | |
callback: callback, | |
context: context | |
}; | |
collection.push(entry); | |
return function () { | |
var io = indexOf(collection, entry); | |
if (io > -1) | |
collection.splice(io, 1); | |
}; | |
}; | |
var iterate = function (collection) { | |
var time = now(); | |
forEach(collection.splice(0), function (entry) { | |
entry.callback.call(entry.context, time); | |
}); | |
}; | |
var defer = function (callback, argument, context) { | |
return kindOf(argument) === 'Number' ? defer.timeout(callback, argument, context) : defer.immediate(callback, argument); | |
}; | |
if (global.process && process.nextTick) { | |
defer.immediate = function (callback, context) { | |
return push(callbacks.immediate, callback, context, process.nextTick); | |
}; | |
} else if (global.setImmediate) { | |
defer.immediate = function (callback, context) { | |
return push(callbacks.immediate, callback, context, setImmediate); | |
}; | |
} else if (global.postMessage && global.addEventListener) { | |
addEventListener('message', function (event) { | |
if (event.source === global && event.data === '@deferred') { | |
event.stopPropagation(); | |
iterate(callbacks.immediate); | |
} | |
}, true); | |
defer.immediate = function (callback, context) { | |
return push(callbacks.immediate, callback, context, function () { | |
postMessage('@deferred', '*'); | |
}); | |
}; | |
} else { | |
defer.immediate = function (callback, context) { | |
return push(callbacks.immediate, callback, context, function (iterator) { | |
setTimeout(iterator, 0); | |
}); | |
}; | |
} | |
var requestAnimationFrame = global.requestAnimationFrame || global.webkitRequestAnimationFrame || global.mozRequestAnimationFrame || global.oRequestAnimationFrame || global.msRequestAnimationFrame || function (callback) { | |
setTimeout(callback, 1000 / 60); | |
}; | |
defer.frame = function (callback, context) { | |
return push(callbacks.frame, callback, context, requestAnimationFrame); | |
}; | |
var clear; | |
defer.timeout = function (callback, ms, context) { | |
var ct = callbacks.timeout; | |
if (!clear) | |
clear = defer.immediate(function () { | |
clear = null; | |
callbacks.timeout = {}; | |
}); | |
return push(ct[ms] || (ct[ms] = []), callback, context, function (iterator) { | |
setTimeout(iterator, ms); | |
}); | |
}; | |
module.exports = defer; | |
}, | |
'node_modules/prime/emitter.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var indexOf = require('node_modules/mout/array/indexOf.js'), forEach = require('node_modules/mout/array/forEach.js'); | |
var prime = require('node_modules/prime/index.js'), defer = require('node_modules/prime/defer.js'); | |
var slice = Array.prototype.slice; | |
var Emitter = prime({ | |
on: function (event, fn) { | |
var listeners = this._listeners || (this._listeners = {}), events = listeners[event] || (listeners[event] = []); | |
if (indexOf(events, fn) === -1) | |
events.push(fn); | |
return this; | |
}, | |
off: function (event, fn) { | |
var listeners = this._listeners, events, key, length = 0; | |
if (listeners && (events = listeners[event])) { | |
var io = indexOf(events, fn); | |
if (io > -1) | |
events.splice(io, 1); | |
if (!events.length) | |
delete listeners[event]; | |
for (var l in listeners) | |
return this; | |
delete this._listeners; | |
} | |
return this; | |
}, | |
emit: function (event) { | |
var self = this, args = slice.call(arguments, 1); | |
var emit = function () { | |
var listeners = self._listeners, events; | |
if (listeners && (events = listeners[event])) { | |
forEach(events.slice(0), function (event) { | |
return event.apply(self, args); | |
}); | |
} | |
}; | |
if (args[args.length - 1] === Emitter.EMIT_SYNC) { | |
args.pop(); | |
emit(); | |
} else { | |
defer(emit); | |
} | |
return this; | |
} | |
}); | |
Emitter.EMIT_SYNC = {}; | |
module.exports = Emitter; | |
}, | |
'node_modules/spotify-events/center.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var Emitter = require('node_modules/prime/emitter.js'); | |
var forIn = require('node_modules/mout/object/forIn.js'); | |
var emitter = new Emitter(); | |
forIn(emitter, function (method, key) { | |
exports[key] = method; | |
}); | |
}, | |
'node_modules/prime/map.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var indexOf = require('node_modules/mout/array/indexOf.js'); | |
var prime = require('node_modules/prime/index.js'); | |
var Map = prime({ | |
constructor: function Map() { | |
this.length = 0; | |
this._values = []; | |
this._keys = []; | |
}, | |
set: function (key, value) { | |
var index = indexOf(this._keys, key); | |
if (index === -1) { | |
this._keys.push(key); | |
this._values.push(value); | |
this.length++; | |
} else { | |
this._values[index] = value; | |
} | |
return this; | |
}, | |
get: function (key) { | |
var index = indexOf(this._keys, key); | |
return index === -1 ? null : this._values[index]; | |
}, | |
count: function () { | |
return this.length; | |
}, | |
forEach: function (method, context) { | |
for (var i = 0, l = this.length; i < l; i++) { | |
if (method.call(context, this._values[i], this._keys[i], this) === false) | |
break; | |
} | |
return this; | |
}, | |
map: function (method, context) { | |
var results = new Map(); | |
this.forEach(function (value, key) { | |
results.set(key, method.call(context, value, key, this)); | |
}, this); | |
return results; | |
}, | |
filter: function (method, context) { | |
var results = new Map(); | |
this.forEach(function (value, key) { | |
if (method.call(context, value, key, this)) | |
results.set(key, value); | |
}, this); | |
return results; | |
}, | |
every: function (method, context) { | |
var every = true; | |
this.forEach(function (value, key) { | |
if (!method.call(context, value, key, this)) | |
return every = false; | |
}, this); | |
return every; | |
}, | |
some: function (method, context) { | |
var some = false; | |
this.forEach(function (value, key) { | |
if (method.call(context, value, key, this)) | |
return !(some = true); | |
}, this); | |
return some; | |
}, | |
indexOf: function (value) { | |
var index = indexOf(this._values, value); | |
return index > -1 ? this._keys[index] : null; | |
}, | |
remove: function (value) { | |
var index = indexOf(this._values, value); | |
if (index !== -1) { | |
this._values.splice(index, 1); | |
this.length--; | |
return this._keys.splice(index, 1)[0]; | |
} | |
return null; | |
}, | |
unset: function (key) { | |
var index = indexOf(this._keys, key); | |
if (index !== -1) { | |
this._keys.splice(index, 1); | |
this.length--; | |
return this._values.splice(index, 1)[0]; | |
} | |
return null; | |
}, | |
keys: function () { | |
return this._keys.slice(); | |
}, | |
values: function () { | |
return this._values.slice(); | |
} | |
}); | |
var map = function () { | |
return new Map(); | |
}; | |
map.prototype = Map.prototype; | |
module.exports = map; | |
}, | |
'node_modules/mout/function/identity.js': function (require, module, exports, global, __filename, __dirname) { | |
function identity(val) { | |
return val; | |
} | |
module.exports = identity; | |
}, | |
'node_modules/mout/function/prop.js': function (require, module, exports, global, __filename, __dirname) { | |
function prop(name) { | |
return function (obj) { | |
return obj[name]; | |
}; | |
} | |
module.exports = prop; | |
}, | |
'node_modules/api/scripts/private/relationsartist.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (hermes, models) { | |
var Relationship = { | |
NOT_SUBSCRIBED: 0, | |
SUBSCRIBED: 1 | |
}; | |
var ARTIST_GRAPH_ROOT = 'hm://socialgraph/'; | |
var ARTIST_GRAPH_SUBSCRIPTIONS = ARTIST_GRAPH_ROOT + 'subscriptions/artist'; | |
var ARTIST_GRAPH_SUBSCRIBERS = ARTIST_GRAPH_ROOT + 'subscribers/artist'; | |
var COMBINED_GRAPH_SUBSCRIPTIONS = ARTIST_GRAPH_ROOT + 'subscriptions/combined'; | |
var _schema = hermes.Schema.fromURL('$api/proto/socialgraph.proto'); | |
function artistSubscriptions(opt_userUri) { | |
var promise = new models.Promise(); | |
var canonicalUsername = opt_userUri ? _trimUri('user', opt_userUri) : ''; | |
if (canonicalUsername == '@') | |
canonicalUsername = ''; | |
var requestPromise = _request('GET', ARTIST_GRAPH_SUBSCRIPTIONS + '/' + canonicalUsername, 'ArtistListReply', 'UserListRequest', { include_length: true }); | |
requestPromise.done(function (data) { | |
var response = [], result = data[0].artists || []; | |
for (var i = 0, len = result.length; i < len; i++) { | |
response.push(models.Artist.fromURI('spotify:artist:' + result[i].artistid)); | |
} | |
promise.setDone(response); | |
}).fail(function (_, error) { | |
promise.setFail(error); | |
}); | |
return promise; | |
} | |
function categorizeUsersAndArtists(uris) { | |
var artistUris = [], userUris = [], artistUriSet = {}, userUriSet = {}, mergedUriSet = {}; | |
for (var i = 0, len = uris.length; i < len; i++) { | |
var uri = uris[i]; | |
if (uri.indexOf('spotify:artist:') === 0) { | |
artistUris.push(uri); | |
} else { | |
userUriSet[uri] = true; | |
userUris.push(uri); | |
} | |
} | |
var promises; | |
promises = models.Artist.fromURIs(artistUris).map(function (artist) { | |
return artist.load('user'); | |
}); | |
var artistsPromise = models.Promise.join(promises); | |
artistsPromise.each(function (artist) { | |
if (artist.user) { | |
userUriSet[artist.user.uri] = true; | |
mergedUriSet[artist.uri] = true; | |
} else { | |
artistUriSet[artist.uri] = true; | |
} | |
}); | |
promises = models.User.fromURIs(userUris).map(function (user) { | |
return user.load('artist'); | |
}); | |
var usersPromise = models.Promise.join(promises); | |
usersPromise.each(function (user) { | |
if (user.artist) { | |
mergedUriSet[user.artist.uri] = true; | |
} | |
}); | |
var promise = new models.Promise(); | |
models.Promise.join(artistsPromise, usersPromise).always(function () { | |
promise.setDone({ | |
artistUris: Object.keys(artistUriSet), | |
userUris: Object.keys(userUriSet), | |
mergedUris: Object.keys(mergedUriSet) | |
}); | |
}); | |
return promise; | |
} | |
function changeRelation(artistUris, relationship) { | |
if (artistUris.length === 0) { | |
var promise = new models.Promise(); | |
promise.setDone(); | |
return promise; | |
} | |
var method = relationship === Relationship.SUBSCRIBED ? 'POST' : 'DELETE'; | |
var promise = _request(method, ARTIST_GRAPH_SUBSCRIPTIONS, 'StringListReply', 'StringListRequest', { args: artistUris.map(_trimArtistUri) }); | |
promise.done(this, function () { | |
models.Artist._cache.update(artistUris, { subscribed: relationship === Relationship.SUBSCRIBED }); | |
}); | |
return promise; | |
} | |
function combinedSubscriptionCount(userUri) { | |
var canonicalUsername = _trimUri('user', userUri); | |
var username; | |
var usernamePromise; | |
if (canonicalUsername === '@') { | |
usernamePromise = models.session.user.load('username').done(function (user) { | |
username = user.username; | |
}).fail(function (_, error) { | |
usernamePromise.setFail(error); | |
}); | |
} else { | |
username = decodeURIComponent(canonicalUsername); | |
usernamePromise = new models.Promise(); | |
usernamePromise.setDone(); | |
} | |
var promise = new models.Promise(); | |
usernamePromise.done(function () { | |
_batchRequest('combinedSubscriptionCount', username, promise); | |
}); | |
return promise; | |
} | |
function updateCache(artistUris, relationship) { | |
models.Artist._cache.update(artistUris, { subscribed: relationship === Relationship.SUBSCRIBED }); | |
} | |
function isSubscribed(artistUri) { | |
return _batchRequest('isSubscribed', _trimArtistUri(artistUri)); | |
} | |
function subscriberCount(artistUri) { | |
return _batchRequest('subscriberCount', _trimArtistUri(artistUri)); | |
} | |
var _batchable = { | |
combinedSubscriptionCount: function (values, promises) { | |
return _request('GET', COMBINED_GRAPH_SUBSCRIPTIONS + '/count', 'CountReply', 'StringListRequest', { args: values }).done(function (frames) { | |
var counts = frames[0].counts; | |
for (var i = 0, len = counts.length; i < len; i++) { | |
promises[i].setDone(counts[i]); | |
} | |
}); | |
}, | |
isSubscribed: function (values, promises) { | |
return _request('GET', ARTIST_GRAPH_SUBSCRIPTIONS + '/exists', 'StringListReply', 'StringListRequest', { args: values }).done(function (frames) { | |
var subscribed = frames[0].reply; | |
for (var i = 0, len = subscribed.length; i < len; i++) { | |
promises[i].setDone(subscribed[i] == 'True'); | |
} | |
}); | |
}, | |
subscriberCount: function (values, promises) { | |
return _request('GET', ARTIST_GRAPH_SUBSCRIBERS + '/count', 'CountReply', 'StringListRequest', { args: values }).done(function (frames) { | |
var counts = frames[0].counts; | |
for (var i = 0, len = counts.length; i < len; i++) { | |
promises[i].setDone(counts[i]); | |
} | |
}); | |
} | |
}; | |
var _batch = {}; | |
var _batchDeferred = false; | |
function _batchRequest(request, value, opt_promise) { | |
if (!opt_promise) { | |
opt_promise = new models.Promise(); | |
} | |
if (_batch[request]) { | |
_batch[request].values.push(value); | |
_batch[request].promises.push(opt_promise); | |
} else { | |
_batch[request] = { | |
values: [value], | |
promises: [opt_promise] | |
}; | |
} | |
if (!_batchDeferred) { | |
SP.defer(null, _runBatchedRequests); | |
_batchDeferred = true; | |
} | |
return opt_promise; | |
} | |
function _request(method, url, opt_replyType, opt_requestType, var_args) { | |
var request = hermes.Hermes.request(method, url, opt_replyType ? [_schema.type(opt_replyType)] : [], opt_requestType ? [_schema.type(opt_requestType)] : []); | |
return request.send.apply(request, SP.varargs(arguments, 4)); | |
} | |
function _runBatchedRequests() { | |
if (_batchDeferred) { | |
_batchDeferred = false; | |
} | |
for (var request in _batch) { | |
var promise = _batchable[request](_batch[request].values, _batch[request].promises); | |
promise.fail(_batch[request], function (_, error) { | |
for (var i = 0, len = this.promises.length; i < len; i++) { | |
this.promises[i].setFail(error); | |
} | |
}); | |
} | |
_batch = {}; | |
} | |
function _trimArtistUri(artistUri) { | |
return _trimUri('artist', artistUri); | |
} | |
function _trimUri(prefix, uri) { | |
var prefix = 'spotify:' + prefix, idx = uri.indexOf(prefix); | |
return idx >= 0 ? uri.substr(idx + prefix.length + 1) : uri; | |
} | |
exports.Relationship = Relationship; | |
exports.artistSubscriptions = artistSubscriptions; | |
exports.categorizeUsersAndArtists = categorizeUsersAndArtists; | |
exports.changeRelation = changeRelation; | |
exports.combinedSubscriptionCount = combinedSubscriptionCount; | |
exports.updateCache = updateCache; | |
exports.isSubscribed = isSubscribed; | |
exports.subscriberCount = subscriberCount; | |
}(require('node_modules/api/scripts/hermes.js'), require('node_modules/api/scripts/models.js'))); | |
}, | |
'node_modules/api/scripts/relations.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (models, relationsartist) { | |
function _combinedSubscriptionsSnapshot(descriptor, offset, length, raw) { | |
var promises = []; | |
var base = descriptor.getBase(); | |
if (base.type != models.ListDescriptor.Types.LIST) { | |
throw new Error('Unexpected descriptor: ' + base.type); | |
} | |
var uri = base.uri; | |
if (length != 0) { | |
var allSubscriptionsPromise = models.Promise.join([ | |
relationsartist.artistSubscriptions(uri), | |
Relations.forUser(models.User.fromURI(uri)).subscriptions.snapshot() | |
]); | |
var itemsPromise = new models.Promise(); | |
allSubscriptionsPromise.done(function (results) { | |
var artistUris = results[0].map(function (artist) { | |
return artist.uri; | |
}); | |
var userUris = results[1].toURIs(); | |
var uris = artistUris.concat(userUris); | |
relationsartist.categorizeUsersAndArtists(uris).done(function (result) { | |
itemsPromise.setDone(result); | |
}).fail(function (_, error) { | |
itemsPromise.setFail(error); | |
}); | |
}); | |
allSubscriptionsPromise.fail(function (_, error) { | |
itemsPromise.setFail(error); | |
}); | |
promises.push(itemsPromise); | |
} | |
var countPromise = relationsartist.combinedSubscriptionCount(uri); | |
promises.push(countPromise); | |
var resultPromise = new models.Promise(); | |
models.Promise.join(promises).done(function () { | |
var result = { array: [] }; | |
if (length != 0) { | |
result.metadata = []; | |
var i, items = itemsPromise.object; | |
for (i = 0; i < items.artistUris.length; i++) { | |
var artistUri = items.artistUris[i]; | |
if (items.mergedUris.indexOf(artistUri) >= 0) | |
continue; | |
result.array.push(artistUri); | |
result.metadata.push({ | |
artist: artistUri, | |
user: null | |
}); | |
} | |
for (i = 0; i < items.userUris.length; i++) { | |
var user = models.User.fromURI(items.userUris[i]); | |
if (user.artist) { | |
result.array.unshift(user.uri); | |
result.metadata.unshift({ | |
artist: user.artist.uri, | |
user: user.uri | |
}); | |
} else { | |
result.array.push(user.uri); | |
result.metadata.push({ | |
artist: null, | |
user: user.uri | |
}); | |
} | |
} | |
if (offset || length > -1) { | |
var end = offset + (length > -1 ? length : result.array.length); | |
result.array = result.array.slice(offset, end); | |
result.metadata = result.metadata.slice(offset, end); | |
} | |
} | |
result.length = countPromise.object; | |
resultPromise.setDone(result); | |
}).fail(function (_, error) { | |
resultPromise.setFail(error); | |
}); | |
return resultPromise; | |
} | |
function Relations(uri) { | |
models.BridgeLoadable.call(this); | |
var owner = models.User.fromURI(uri); | |
this.resolve('owner', owner); | |
this.resolve('subscribers', new models.BridgeCollection(models.User, null, 'relations_subscribers_users', owner.uri)); | |
this.resolve('subscriptions', new models.BridgeCollection(models.User, null, 'relations_subscriptions_users', owner.uri)); | |
this.resolve('combinedSubscriptions', new models.Collection(models.Profile, null, _combinedSubscriptionsSnapshot, owner.uri)); | |
} | |
SP.inherit(Relations, models.BridgeLoadable); | |
Relations.fromURI = models.Cache.lookup; | |
Relations._cache = new models.Cache(Relations); | |
models.Loadable.define(Relations, [ | |
'owner', | |
'subscribers', | |
'subscriptions', | |
'combinedSubscriptions' | |
]); | |
function CurrentUserRelations(uri) { | |
Relations.call(this, uri); | |
this.resolve('blocked', new models.BridgeCollection(models.User, null, 'relations_blocked_users', this.owner.uri)); | |
this.resolve('dismissed', new models.BridgeCollection(models.User, null, 'relations_dismissed_users', this.owner.uri)); | |
this.resolve('hidden', new models.BridgeCollection(models.User, null, 'relations_hidden_users', this.owner.uri)); | |
models.User.getOrCreateRelationsListener().proxyTo(this); | |
} | |
SP.inherit(CurrentUserRelations, Relations); | |
models.Loadable.define(CurrentUserRelations, [ | |
'blocked', | |
'dismissed', | |
'hidden' | |
]); | |
CurrentUserRelations.prototype.block = function (users) { | |
return models.promisedRequest(this, 'relations_block', [this.owner.uri].concat(SP.uris(arguments))); | |
}; | |
CurrentUserRelations.prototype.dismiss = function (users) { | |
return models.promisedRequest(this, 'relations_dismiss', [this.owner.uri].concat(SP.uris(arguments))); | |
}; | |
CurrentUserRelations.prototype.subscribe = function (profiles) { | |
return this._changeRelation(SP.uris(arguments), relationsartist.Relationship.SUBSCRIBED); | |
}; | |
CurrentUserRelations.prototype.undismiss = function (users) { | |
return models.promisedRequest(this, 'relations_undismiss', [this.owner.uri].concat(SP.uris(arguments))); | |
}; | |
CurrentUserRelations.prototype.unblock = function (users) { | |
return models.promisedRequest(this, 'relations_unblock', [this.owner.uri].concat(SP.uris(arguments))); | |
}; | |
CurrentUserRelations.prototype.unsubscribe = function (profiles) { | |
return this._changeRelation(SP.uris(arguments), relationsartist.Relationship.NOT_SUBSCRIBED); | |
}; | |
CurrentUserRelations.prototype._changeRelation = function (uris, relationship) { | |
var promise = new models.Promise(); | |
var suffix = relationship === relationsartist.Relationship.SUBSCRIBED ? 'subscribe' : 'unsubscribe'; | |
relationsartist.categorizeUsersAndArtists(uris).done(this, function (result) { | |
var promises = []; | |
if (result.userUris.length) { | |
var subscribeUsersPromise = models.promisedRequest(this, 'relations_' + suffix, [this.owner.uri].concat(result.userUris)); | |
subscribeUsersPromise.done(this, function () { | |
relationsartist.updateCache(result.mergedUris, relationship); | |
}); | |
promises.push(subscribeUsersPromise); | |
} | |
if (result.artistUris.length) { | |
var subscribeArtistsPromise = relationsartist.changeRelation(result.artistUris, relationship); | |
subscribeArtistsPromise.done(this, function (frames) { | |
var uris = result.artistUris; | |
if (frames[0] && frames[0].reply) { | |
uris = uris.filter(function (artistUri) { | |
return frames[0].reply.indexOf(artistUri.substr(15)) == -1; | |
}); | |
} | |
if (!uris.length) | |
return; | |
this.eventDone({ | |
type: relationship === relationsartist.Relationship.SUBSCRIBED ? 'add' : 'remove', | |
receiver: 'combinedSubscriptions', | |
uris: uris | |
}); | |
}); | |
promises.push(subscribeArtistsPromise); | |
} | |
models.Promise.join(promises).done(this, function () { | |
promise.setDone(this); | |
}).fail(function (_, error) { | |
promise.setFail(error); | |
}); | |
}); | |
return promise; | |
}; | |
Relations._currentUser = null; | |
Relations.forCurrentUser = function () { | |
if (!Relations._currentUser) { | |
Relations._currentUser = new CurrentUserRelations(models.session.user.uri); | |
} | |
return Relations._currentUser; | |
}; | |
Relations.forUser = function (user) { | |
return Relations.fromURI(user.uri); | |
}; | |
models.Loadable.define(models.Artist, ['subscribed'], '_relations__temp_patch'); | |
models.Artist.prototype._relations__temp_patch = function (propsMask) { | |
relationsartist.isSubscribed(this.uri).done(this, function (subscribed) { | |
this.resolveMany(propsMask, { subscribed: subscribed }); | |
}).fail(this, function () { | |
this.resolveFail(propsMask, { error: 'Cannot load subscribed property' }); | |
}); | |
}; | |
function _userRelationChangeHandler(evt) { | |
for (var i = 0; i < evt.uris.length; i++) { | |
models.User.fromURI(evt.uris[i]).load('artist').done(function (user) { | |
if (!user.artist) | |
return; | |
user.artist.resolve('subscribed', evt.type == 'add'); | |
}); | |
} | |
Relations.forCurrentUser().eventDone({ | |
type: evt.type, | |
receiver: 'combinedSubscriptions', | |
uris: evt.uris | |
}); | |
} | |
var subs = Relations.forCurrentUser().subscriptions; | |
subs.addEventListener('add', _userRelationChangeHandler); | |
subs.addEventListener('remove', _userRelationChangeHandler); | |
exports.Relations = Relations; | |
}(require('node_modules/api/scripts/models.js'), require('node_modules/api/scripts/private/relationsartist.js'))); | |
}, | |
'node_modules/views/scripts/buttons.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (device, Library, models, Toplist, Relations, Popup, buttonsStrings, css, dom, dnd, touch, ContextApp, frame, Logger) { | |
var _ = SP.bind(buttonsStrings.get, buttonsStrings); | |
exports.Button = Button; | |
exports.CustomButton = CustomButton; | |
exports.ShareButton = ShareButton; | |
exports.StartRadioButton = StartRadioButton; | |
exports.SubscribeButton = SubscribeButton; | |
exports.QuickActionButtons = QuickActionButtons; | |
exports.AddToCollectionButton = AddToCollectionButton; | |
exports.PlayButton = PlayButton; | |
if (device.mobile) { | |
var preloadImg = document.createElement('img'); | |
preloadImg.src = '$views/img/list-mobile-contextbutton' + (window.devicePixelRatio === 2 ? '@2x' : '') + '.png'; | |
} | |
if (dnd.drag.hasDragSupport) { | |
window.addEventListener('dragstart', function (e) { | |
var element = e.target; | |
if (element.hasAttribute('data-sp-button')) { | |
e.preventDefault(); | |
} | |
}, false); | |
} | |
function BaseButton(cssClass, opt_label, opt_icon) { | |
this.accentuated = false; | |
this.disabled = false; | |
this._accentuatedEffect = null; | |
this._nodeClass = cssClass; | |
this._setupTouch(); | |
this._buildButton(); | |
this._addBehavior(); | |
this.label = ''; | |
if (typeof opt_label === 'string') { | |
this.setLabel(opt_label); | |
} | |
this.icon = ''; | |
if (typeof opt_icon === 'string') { | |
this.setIcon(opt_icon); | |
} | |
this.addEventListener('click', this._clicked); | |
} | |
SP.inherit(BaseButton, models.Observable); | |
var measureNode = document.createElement('button'); | |
measureNode.style.position = 'absolute'; | |
measureNode.style.visibility = 'hidden'; | |
var measureNodeSpan = document.createElement('span'); | |
measureNodeSpan.className = 'sp-button-text'; | |
measureNode.appendChild(measureNodeSpan); | |
var labelMeasurements = {}; | |
BaseButton.prototype.getTextWidth = function (text, opt_cssClass) { | |
var cssClass = opt_cssClass || 'sp-no-classname'; | |
if (labelMeasurements[text] && labelMeasurements[text][cssClass]) { | |
return labelMeasurements[text][cssClass]; | |
} | |
document.body.appendChild(measureNode); | |
measureNode.className = opt_cssClass || this.node.className; | |
measureNodeSpan.textContent = text; | |
var width = measureNodeSpan.getBoundingClientRect().width; | |
document.body.removeChild(measureNode); | |
if (!labelMeasurements[text]) | |
labelMeasurements[text] = {}; | |
labelMeasurements[text][cssClass] = width; | |
return width; | |
}; | |
BaseButton.prototype.setWidthFromLabels = function (labels, opt_icons) { | |
var padding = this._touchDevice ? 0 : 4; | |
var iconWidth = this._touchDevice ? 46 : 20; | |
var widths = []; | |
var icons = opt_icons || []; | |
for (var i = 0, l = labels.length; i < l; i++) { | |
icons[i] = icons[i] ? iconWidth : 0; | |
widths.push(this.getTextWidth(labels[i]) + icons[i]); | |
} | |
var width = Math.max.apply(Math, widths); | |
this.node.style.minWidth = width + padding * 2 + 'px'; | |
}; | |
BaseButton.prototype.setLabel = function (label) { | |
this.label = label; | |
this._label.data = label || ''; | |
}; | |
BaseButton.prototype.setIcon = function (url, opt_cssClass) { | |
if (typeof opt_cssClass === 'string') { | |
this.setIconClass(opt_cssClass); | |
} | |
this.icon = url; | |
this._icon.style.backgroundImage = url ? 'url("' + url + '")' : null; | |
this._icon.style.display = url ? 'inline-block' : 'none'; | |
}; | |
BaseButton.prototype.setIconClass = function (cssClass) { | |
css.removeClass(this._icon, this._iconClass); | |
css.addClass(this._icon, cssClass); | |
this._iconClass = cssClass; | |
}; | |
BaseButton.prototype.setDisabled = function (disabled) { | |
this.disabled = !!disabled; | |
if (this.disabled) { | |
this.node.setAttribute('disabled', 'disabled'); | |
} else { | |
this.node.removeAttribute('disabled'); | |
} | |
}; | |
BaseButton._accentuationEffects = { | |
'positive': 'sp-button-accentuated-positive', | |
'negative': 'sp-button-accentuated-negative' | |
}; | |
BaseButton.prototype.setAccentuated = function (accentuated, opt_effect) { | |
this.accentuated = accentuated; | |
var effects = BaseButton._accentuationEffects; | |
if (this._accentuatedEffect in effects) { | |
css.removeClass(this.node, effects[this._accentuatedEffect]); | |
this._accentuatedEffect = null; | |
} | |
if (accentuated) { | |
var defaultEffect = 'positive'; | |
var effect = effects[opt_effect] ? opt_effect : defaultEffect; | |
var effectCSSClass = effects[effect]; | |
css.addClass(this.node, 'sp-button-accentuated'); | |
css.addClass(this.node, effectCSSClass); | |
this._accentuatedEffect = effect; | |
} else { | |
css.removeClass(this.node, 'sp-button-accentuated'); | |
} | |
}; | |
BaseButton.prototype.contains = function (element) { | |
var node = this.node; | |
if (node === element || node.contains && node.contains(element)) | |
return true; | |
var body = document.body; | |
while (element && element !== body) { | |
if (element === node) | |
return true; | |
element = element.parentNode; | |
} | |
return false; | |
}; | |
BaseButton.prototype._setupTouch = function () { | |
this._touchDevice = device.touch && device.mobile; | |
this.touchPreventsScrolling = false; | |
}; | |
BaseButton.prototype._buildButton = function () { | |
this.node = document.createElement('button'); | |
this.node.setAttribute('type', 'button'); | |
if (dnd.drag.hasDragSupport) { | |
this.node.setAttribute('draggable', 'true'); | |
} | |
this.node.setAttribute('data-sp-button', 'true'); | |
css.addClass(this.node, this._nodeClass); | |
if (this._touchDevice) { | |
css.addClass(this.node, 'sp-button-touch'); | |
} | |
if (this._touchDevice) { | |
var hitArea = document.createElement('span'); | |
css.addClass(hitArea, 'sp-button-hitarea'); | |
this.node.appendChild(hitArea); | |
this.hitArea = hitArea; | |
} else { | |
this.hitArea = this.node; | |
} | |
var text = document.createElement('span'); | |
css.addClass(text, 'sp-button-text'); | |
this.node.appendChild(text); | |
this._icon = document.createElement('div'); | |
css.addClass(this._icon, 'sp-button-icon'); | |
if (this.icon) { | |
this._icon.style.backgroundImage = 'url("' + this.icon + '")'; | |
} | |
text.appendChild(this._icon); | |
this._label = document.createTextNode(this.label || ''); | |
text.appendChild(this._label); | |
}; | |
BaseButton.prototype._addBehavior = function () { | |
var self = this; | |
if (this._touchDevice) { | |
touch.selection.on('select', this.hitArea, function (event) { | |
if (self.disabled) | |
return; | |
self._active = true; | |
self._pushButton(); | |
event.stopPropagation(); | |
}); | |
touch.selection.on('deselect', this.hitArea, function (event) { | |
self._releaseButton(); | |
}); | |
touch.selection.on('tap', this.hitArea, function (event) { | |
if (self.disabled || !self._active) | |
return; | |
event.stopPropagation(); | |
self._active = false; | |
self.dispatchEvent('pointerend'); | |
self.dispatchEvent({ | |
type: 'click', | |
browserEvent: event | |
}); | |
}); | |
} else { | |
this.hitArea.addEventListener('mousedown', function (event) { | |
self._startHandler(event); | |
}); | |
this._boundMoveHandler = function (event) { | |
self._moveHandler(event); | |
}; | |
this._boundEndHandler = function (event) { | |
self._endHandler(event); | |
}; | |
} | |
}; | |
BaseButton.prototype._startHandler = function (event) { | |
if (this.disabled) { | |
return; | |
} | |
if (event.button !== undefined && event.button !== 0) | |
return; | |
BaseButton._isMac = BaseButton._isMac || navigator.userAgent.indexOf('Macintosh') > -1 ? true : false; | |
if (BaseButton._isMac && event.ctrlKey) | |
return; | |
if (!this._touchDevice) { | |
this._buttonPos = this._getPos(); | |
document.addEventListener('mousemove', this._boundMoveHandler); | |
document.addEventListener('mouseup', this._boundEndHandler); | |
} | |
this._active = true; | |
this._pushButton(); | |
event.stopPropagation(); | |
}; | |
BaseButton.prototype._moveHandler = function (event) { | |
if (!this.disabled && this._active) { | |
event.stopPropagation(); | |
if (this._isPointerInside(event)) | |
this._pushButton(); | |
else | |
this._releaseButton(); | |
} | |
}; | |
BaseButton.prototype._endHandler = function (event) { | |
document.removeEventListener('mousemove', this._boundMoveHandler); | |
document.removeEventListener('mouseup', this._boundEndHandler); | |
if (!this.disabled && this._active) { | |
event.stopPropagation(); | |
this._active = false; | |
this._releaseButton(); | |
this.dispatchEvent('pointerend'); | |
if (this._isPointerInside(event)) { | |
this.dispatchEvent({ | |
type: 'click', | |
browserEvent: event | |
}); | |
} | |
} | |
}; | |
BaseButton.prototype._pushButton = function () { | |
css.addClass(this.node, 'sp-button-active'); | |
}; | |
BaseButton.prototype._releaseButton = function () { | |
css.removeClass(this.node, 'sp-button-active'); | |
}; | |
BaseButton.prototype._getPos = function () { | |
var scrollValues = this._scrollValues || {}; | |
scrollValues.x = window.pageXOffset || document.documentElement.scrollLeft; | |
scrollValues.y = window.pageYOffset || document.documentElement.scrollTop; | |
this._scrollValues = scrollValues; | |
var buttonPos = this._buttonPos || {}; | |
var rect = this.hitArea.getBoundingClientRect(); | |
buttonPos.left = rect.left + scrollValues.x; | |
buttonPos.top = rect.top + scrollValues.y; | |
buttonPos.right = buttonPos.left + rect.width; | |
buttonPos.bottom = buttonPos.top + rect.height; | |
return buttonPos; | |
}; | |
BaseButton.prototype._getPointerPos = function (event) { | |
return { | |
x: this._touchDevice ? event.changedTouches[0].pageX : event.pageX, | |
y: this._touchDevice ? event.changedTouches[0].pageY : event.pageY | |
}; | |
}; | |
BaseButton.prototype._isPointerInside = function (event) { | |
var pos = this._getPointerPos(event); | |
var buttonPos = this._buttonPos || this._getPos(); | |
var isInsideX = pos.x > buttonPos.left && pos.x <= buttonPos.right; | |
var isInsideY = pos.y > buttonPos.top && pos.y <= buttonPos.bottom; | |
return isInsideX && isInsideY; | |
}; | |
function Button(opt_label, opt_icon) { | |
BaseButton.call(this, 'sp-button', opt_label, opt_icon); | |
this.size = 'normal'; | |
} | |
SP.inherit(Button, BaseButton); | |
Button.withLabel = function (opt_label, opt_icon) { | |
return new Button(opt_label, opt_icon); | |
}; | |
Button._sizes = { | |
normal: '', | |
small: 'sp-button-small', | |
large: 'sp-button-large' | |
}; | |
Button.prototype.setSize = function (size) { | |
if (this.size == size) | |
return; | |
if (!(size in Button._sizes)) { | |
throw new Error(size + ' is not a valid size'); | |
} | |
css.removeClass(this.node, Button._sizes[this.size]); | |
css.addClass(this.node, Button._sizes[size]); | |
this.size = size; | |
}; | |
Button.prototype._buildButton = function () { | |
Button._superClass._buildButton.call(this); | |
var bg = document.createElement('span'); | |
css.addClass(bg, 'sp-button-background'); | |
this.node.appendChild(bg); | |
}; | |
var player = models.player; | |
function PlayButton(item, opt_options) { | |
var options = { | |
size: 'medium', | |
position: undefined, | |
context: null, | |
index: -1, | |
getIndexInContext: function () { | |
return -1; | |
} | |
}; | |
if (opt_options) { | |
for (var prop in opt_options) { | |
if (opt_options.hasOwnProperty(prop)) { | |
options[prop] = opt_options[prop]; | |
} | |
} | |
} | |
this.options = options; | |
if (device.container === 'ios' && options.size !== 'medium') { | |
options.size = 'medium'; | |
} | |
var baseClass = this._baseClass = 'sp-button-play'; | |
PlayButton._superClass.constructor.call(this, baseClass); | |
this.setItem(item, options.context, options.getIndexInContext, options.getContextGroupData); | |
this.setSize(options.size); | |
this.setCentered(options.position === 'centered'); | |
var self = this; | |
player.load([ | |
'playing', | |
'track', | |
'context' | |
]).done(function () { | |
var timeout; | |
var changeState = function () { | |
if (self.node.parentNode) { | |
self._changeState(); | |
clearTimeout(timeout); | |
} else { | |
timeout = setTimeout(changeState, 100); | |
} | |
}; | |
changeState(); | |
}); | |
player.addEventListener('change', function () { | |
self._changeState(); | |
}); | |
this.addEventListener('click', function () { | |
if (self._playing) { | |
self._pause(); | |
self.dispatchEvent('pause-click'); | |
} else { | |
self._play(); | |
self.dispatchEvent('play-click'); | |
} | |
}); | |
} | |
SP.inherit(PlayButton, BaseButton); | |
var playButtonSizes = [ | |
'xs', | |
'small', | |
'medium', | |
'large', | |
'xl' | |
]; | |
PlayButton.prototype.setSize = function (size) { | |
if (playButtonSizes.indexOf(size) === -1) | |
return; | |
if (size === this._size) | |
return; | |
css.removeClass(this.node, this._baseClass + '-' + this._size); | |
this._size = size; | |
css.addClass(this.node, this._baseClass + '-' + size); | |
}; | |
PlayButton.prototype.setCentered = function (enable) { | |
var value = enable ? 'centered' : undefined; | |
if (this._position === value) | |
return; | |
this._position = value; | |
if (enable) { | |
css.addClass(this.node, this._baseClass + '-centered'); | |
} else { | |
css.removeClass(this.node, this._baseClass + '-centered'); | |
} | |
}; | |
PlayButton.prototype.setItem = function (item, opt_context, opt_getIndexInContext, opt_getContextGroupData) { | |
var self = this; | |
this.item = item; | |
this._user = null; | |
var uri = this._uri = item.uri; | |
this.setContext(opt_context, opt_getIndexInContext, opt_getContextGroupData); | |
this._type = 'context'; | |
if (item instanceof models.Track) | |
this._type = 'track'; | |
else if (item instanceof models.Artist) | |
this._type = 'artist'; | |
var hasContext = this.context || this._type === 'context'; | |
if (hasContext) { | |
var context = this.context || this.item; | |
var match = context.uri.match(/^spotify:user:(.*?):/); | |
if (match) | |
this._user = match[1]; | |
} | |
if (this._type === 'artist') { | |
match = uri.match(/^spotify:artist:(\w+)/); | |
if (match) { | |
this._uri = 'spotify:artist:' + match[1]; | |
Toplist.forArtist(this.item).load('tracks').done(function (toplist) { | |
self.item = toplist.tracks; | |
self._changeState(); | |
}); | |
} | |
} | |
this._changeState(); | |
}; | |
PlayButton.prototype.setContext = function (opt_context, opt_getIndexInContext, opt_getContextGroupData) { | |
this.context = opt_context || null; | |
this.options.getIndexInContext = opt_getIndexInContext; | |
this.options.getContextGroupData = opt_getContextGroupData; | |
}; | |
PlayButton.prototype._isPlaying = function () { | |
if (!player.track) | |
return false; | |
var playerContextURI = player.context && player.context.uri || ''; | |
if (this._user) { | |
playerContextURI = playerContextURI.replace(':@:', ':' + this._user + ':'); | |
} | |
if (this._type === 'track') { | |
var isSameUri = player.track && player.track.uri === this.item.uri; | |
var isSameIndex = this.options.getIndexInContext && player.index === this.options.getIndexInContext(this.item, this.context, this.node); | |
var isTrackContext = playerContextURI === player.track.uri; | |
var bothHaveContext = playerContextURI && this.context; | |
var noneHaveContext = !playerContextURI && !this.context; | |
var isSameContext = false; | |
if (noneHaveContext) { | |
isSameContext = true; | |
} else if (bothHaveContext) { | |
isSameContext = playerContextURI === this.context.uri || playerContextURI === this.context.uri + ':tracks'; | |
} else if (isSameUri && isTrackContext) { | |
isSameContext = true; | |
} | |
var isSameTrack = bothHaveContext ? isSameIndex : isSameUri; | |
return isSameTrack && isSameContext; | |
} | |
if (!player.context || !player.context.uri) | |
return false; | |
if (this._type === 'artist') { | |
playerContextURI = playerContextURI.replace(/:top:tracks$/, ''); | |
} | |
var index = this.options.index; | |
if (this._type === 'context' && (index > -1 && player.index !== index)) | |
return false; | |
return playerContextURI === this._uri; | |
}; | |
PlayButton.prototype._changeState = function (opt_playing) { | |
var playing = opt_playing; | |
var node = this.node; | |
var baseClass = this._baseClass; | |
if (playing === undefined) { | |
playing = player.playing; | |
if (!this._isPlaying()) | |
playing = false; | |
} | |
if (playing && !this._playing) { | |
this._playing = true; | |
css.addClass(node, baseClass + '-pause'); | |
this.dispatchEvent('play'); | |
} else if (!playing && this._playing) { | |
this._playing = false; | |
css.removeClass(node, baseClass + '-pause'); | |
this.dispatchEvent('pause'); | |
} | |
}; | |
PlayButton.prototype._play = function () { | |
if (!this._playing) { | |
this._changeState(true); | |
if (this._isPlaying()) { | |
player.play(); | |
} else { | |
if (this.options.getContextGroupData) { | |
var contextGroupData = this.options.getContextGroupData(); | |
if (contextGroupData.group) { | |
var indexInContext = this.options.getIndexInContext ? this.options.getIndexInContext(this.item, this.context, this.node) : 0; | |
player.playContextGroup(contextGroupData.group, contextGroupData.index || 0, indexInContext).fail(this, function (a, e) { | |
this._changeState(false); | |
}); | |
} | |
} else if (this._type === 'track') { | |
if (this.context) { | |
var index = this.options.getIndexInContext(this.item, this.context, this.node); | |
index = index > -1 ? index : null; | |
player.playContext(this.context, index).fail(this, function () { | |
this._changeState(false); | |
}); | |
} else { | |
player.playTrack(this.item).fail(this, function () { | |
this._changeState(false); | |
}); | |
} | |
} else { | |
player.playContext(this.item, this.options.index).fail(this, function () { | |
this._changeState(false); | |
}); | |
} | |
} | |
} | |
}; | |
PlayButton.prototype._pause = function () { | |
if (this._playing) { | |
this._changeState(false); | |
player.pause(); | |
} | |
}; | |
PlayButton.forItem = function (item, opt_options) { | |
return new PlayButton(item, opt_options); | |
}; | |
function CustomButton(cssClass, label, icon) { | |
BaseButton.call(this, 'sp-button-empty ' + cssClass, label, icon); | |
} | |
SP.inherit(CustomButton, BaseButton); | |
CustomButton.withClass = function (cssClass, opt_label, opt_icon) { | |
return new CustomButton(cssClass, opt_label, opt_icon); | |
}; | |
function ShareButton(item) { | |
Button.call(this, _('ButtonShare')); | |
this.setIconClass('sp-icon-share'); | |
this.item = item; | |
} | |
SP.inherit(ShareButton, Button); | |
ShareButton.forAlbum = function (album) { | |
if (!(album instanceof models.Album)) | |
throw new Error('not an Album'); | |
return new ShareButton(album); | |
}; | |
ShareButton.forArtist = function (artist) { | |
if (!(artist instanceof models.Artist)) | |
throw new Error('not an Artist'); | |
return new ShareButton(artist); | |
}; | |
ShareButton.forPlaylist = function (playlist) { | |
if (!(playlist instanceof models.Playlist)) | |
throw new Error('not a Playlist'); | |
return new ShareButton(playlist); | |
}; | |
ShareButton.forTrack = function (track) { | |
if (!(track instanceof models.Track)) | |
throw new Error('not a Track'); | |
return new ShareButton(track); | |
}; | |
ShareButton.prototype._clicked = function () { | |
var message = ''; | |
var rect = this.node.getBoundingClientRect(); | |
var x = rect.left + rect.width / 2; | |
var y = rect.top + rect.height / 2; | |
var point = { | |
x: x, | |
y: y | |
}; | |
models.client.showShareUI(this.item.uri, message, point); | |
}; | |
function StartRadioButton(item) { | |
Button.call(this, _('ButtonStartRadio')); | |
this.setIconClass('sp-icon-radio'); | |
this.item = item; | |
} | |
SP.inherit(StartRadioButton, Button); | |
StartRadioButton.forAlbum = function (album) { | |
if (!(album instanceof models.Album)) | |
throw new Error('not an Album'); | |
return new StartRadioButton(album); | |
}; | |
StartRadioButton.forArtist = function (artist) { | |
if (!(artist instanceof models.Artist)) | |
throw new Error('not an Artist'); | |
return new StartRadioButton(artist); | |
}; | |
StartRadioButton.forPlaylist = function (playlist) { | |
if (!(playlist instanceof models.Playlist)) | |
throw new Error('not a Playlist'); | |
return new StartRadioButton(playlist); | |
}; | |
StartRadioButton.forTrack = function (track) { | |
if (!(track instanceof models.Track)) | |
throw new Error('not a Track'); | |
return new StartRadioButton(track); | |
}; | |
StartRadioButton.prototype._clicked = function () { | |
var uri = this.item.uri.replace(/^spotify:/, 'spotify:radio:'); | |
models.application.openURI(uri); | |
}; | |
function SubscribeButton(item, opt_options) { | |
if (!opt_options) | |
opt_options = {}; | |
var size = opt_options.size || ''; | |
this._useIcon = opt_options.icon === undefined ? true : !!opt_options.icon; | |
this._fullLabel = !!opt_options.fullLabel; | |
this._initialFollowState = opt_options.initialFollowState === undefined ? null : !!opt_options.initialFollowState; | |
var self = this; | |
Button.call(this, ''); | |
this.setDisabled(true); | |
this._setLabels(item); | |
if (size) | |
css.addClass(this.node, 'sp-button-' + size); | |
css.addClass(this.node, 'sp-button-subscribe'); | |
this._pending = false; | |
this._subscribed = null; | |
this._isAutoAccentuated = true; | |
this._isLimited = null; | |
var setMode = function (e, hover) { | |
self.setAccentuated(!!hover, !!hover ? 'negative' : undefined); | |
self.setLabel(!!hover ? self._subscribeLabels.unsubscribe : self._subscribeLabels.subscribed); | |
}; | |
this.addEventListener('pointerend', function (e) { | |
if (self._subscribed) { | |
setMode(e, false); | |
} | |
}); | |
if (!this._touchDevice) { | |
var mouseHandler = function (e) { | |
if (self.disabled) { | |
return; | |
} | |
self._isMouseHovering = e.type === 'mouseover'; | |
if (self._subscribed) { | |
if (self._active && self._isMouseHovering || !self._active) { | |
setMode(e, self._isMouseHovering); | |
} | |
} | |
}; | |
dom.addEventListener(this.node, 'mouseover', mouseHandler); | |
dom.addEventListener(this.node, 'mouseout', mouseHandler); | |
this._mouseHandler = mouseHandler; | |
} | |
this._setSubscribed(false); | |
this.setItem(item); | |
} | |
SP.inherit(SubscribeButton, Button); | |
SubscribeButton.prototype.setItem = function (item) { | |
var self = this; | |
if (this.item) { | |
this.item.removeEventListener('change:subscribed', this._update); | |
} | |
this.item = item; | |
this._itemLoaded = true; | |
var onLoadedCallbackRun = false; | |
if (item instanceof models.Profile) { | |
this._itemLoaded = false; | |
item.load('artist', 'user').done(function () { | |
if (item.user) { | |
self.item = item.user; | |
} else if (item.artist) { | |
self.item = item.artist; | |
} | |
self._itemLoaded = true; | |
onLoaded(); | |
}); | |
} | |
function onLoaded() { | |
if (onLoadedCallbackRun) | |
return; | |
onLoadedCallbackRun = true; | |
if (self._fullLabel) { | |
if (item.user && item.artist) { | |
self._setLabels(item.artist); | |
} else { | |
self._setLabels(self.item); | |
} | |
} | |
function update(isSubscribed) { | |
self._setSubscribed(isSubscribed); | |
} | |
self._update = function () { | |
update(self.item.subscribed); | |
}; | |
function done(isSubscribed) { | |
update(isSubscribed); | |
self.setDisabled(false); | |
frame.request(function () { | |
self.dispatchEvent('load'); | |
}); | |
self.item.addEventListener('change:subscribed', self._update); | |
} | |
models.client.load('features').done(function (client) { | |
self._isLimited = client.features['limitedSubscribe']; | |
if (self._initialFollowState !== null) { | |
done(self._initialFollowState); | |
if (!self._isLimited) { | |
self.item.load('subscribed').done(function () { | |
self._update(); | |
}); | |
} | |
} else { | |
if (!self._isLimited) { | |
self.item.load('subscribed').done(function () { | |
done(self.item.subscribed); | |
}); | |
} else { | |
done(false); | |
} | |
} | |
}); | |
} | |
; | |
if (this._itemLoaded) | |
onLoaded(); | |
}; | |
SubscribeButton.prototype.setSize = function (size) { | |
SubscribeButton._superClass.setSize.call(this, size); | |
this.setWidthFromLabels(this._subscribeLabels, this._subscribeIcons); | |
}; | |
SubscribeButton.prototype.setAutoAccentuated = function (autoAccentuated) { | |
this._isAutoAccentuated = !!autoAccentuated; | |
if (this._isAutoAccentuated) { | |
this.setAccentuated(!this._subscribed); | |
} else { | |
this.setAccentuated(false); | |
} | |
}; | |
SubscribeButton.prototype._setLabels = function (item) { | |
var subscribeLabel, unsubscribeLabel, subscribedLabel; | |
if (this._fullLabel) { | |
if (item instanceof models.Artist) { | |
subscribeLabel = _('ButtonSubscribeArtist'); | |
unsubscribeLabel = _('ButtonUnsubscribeArtist'); | |
subscribedLabel = _('ButtonSubscribedArtist'); | |
} else if (item instanceof models.Playlist) { | |
subscribeLabel = _('ButtonSubscribePlaylist'); | |
unsubscribeLabel = _('ButtonUnsubscribePlaylist'); | |
subscribedLabel = _('ButtonSubscribedPlaylist'); | |
} else if (item instanceof models.User) { | |
subscribeLabel = _('ButtonSubscribeUser'); | |
unsubscribeLabel = _('ButtonUnsubscribeUser'); | |
subscribedLabel = _('ButtonSubscribedUser'); | |
} | |
} else { | |
subscribeLabel = _('ButtonSubscribe'); | |
unsubscribeLabel = _('ButtonUnsubscribe'); | |
subscribedLabel = _('ButtonSubscribed'); | |
} | |
var _labels = this._subscribeLabels = { | |
subscribe: subscribeLabel, | |
subscribed: subscribedLabel | |
}; | |
var _icons = this._subscribeIcons = { | |
subscribe: this._useIcon, | |
subscribed: false | |
}; | |
var labels = [ | |
_labels.subscribe, | |
_labels.subscribed | |
]; | |
var icons = [ | |
_icons.subscribe, | |
_icons.subscribed | |
]; | |
if (!this._touchDevice) { | |
_labels.unsubscribe = unsubscribeLabel; | |
_icons.unsubscribe = false; | |
labels.push(_labels.unsubscribe); | |
icons.push(_icons.unsubscribe); | |
} | |
this.setWidthFromLabels(labels, icons); | |
}; | |
SubscribeButton.prototype._clicked = function () { | |
if (!this._itemLoaded) | |
return; | |
var subscribed = this.item.subscribed; | |
if (this._isLimited) { | |
SP.request('client_show_limited_feature_ui', ['limitedSubscribe'], function () { | |
}, function () { | |
}); | |
return; | |
} | |
if (typeof subscribed != 'boolean' || this._pending) | |
return; | |
this._setSubscribed(!subscribed); | |
this._updateSubscription(!subscribed); | |
}; | |
SubscribeButton.prototype._call = function (object, method) { | |
if (!this._itemLoaded) | |
return; | |
this._pending = true; | |
return object[method](this.item).done(this, this._done).fail(this, this._fail); | |
}; | |
SubscribeButton.prototype._done = function () { | |
this._pending = false; | |
this.dispatchEvent({ | |
type: this._subscribed ? 'subscribe' : 'unsubscribe', | |
item: this.item | |
}); | |
}; | |
SubscribeButton.prototype._fail = function (error) { | |
this._pending = false; | |
this.dispatchEvent({ | |
type: (this._subscribed ? 'subscribe' : 'unsubscribe') + '-fail', | |
item: this.item, | |
error: error | |
}); | |
this._setSubscribed(this.item.subscribed); | |
}; | |
SubscribeButton.prototype._setSubscribed = function (subscribe) { | |
if (!this._itemLoaded) | |
return; | |
if (this._subscribed === subscribe) | |
return; | |
this._subscribed = subscribe; | |
if (this._isAutoAccentuated) { | |
this.setAccentuated(!subscribe); | |
} | |
this.setLabel(subscribe ? this._subscribeLabels.subscribed : this._subscribeLabels.subscribe); | |
if (this._useIcon) { | |
this.setIconClass(subscribe ? '' : 'sp-icon-add'); | |
} | |
if (this._isMouseHovering) { | |
this._mouseHandler.call(this.node, { type: 'mouseover' }); | |
} | |
}; | |
SubscribeButton.prototype._updateSubscription = function (subscribe) { | |
throw new Error('SubscribeButton _updateSubscription not implemented'); | |
}; | |
function SubscribePlaylistButton(playlist, opt_options) { | |
if (!(playlist instanceof models.Playlist)) | |
throw new Error('not a Playlist'); | |
SubscribeButton.call(this, playlist, opt_options); | |
} | |
SP.inherit(SubscribePlaylistButton, SubscribeButton); | |
SubscribePlaylistButton.prototype._showAutoFollowPopup = function (isAviciiPlaylist) { | |
var self = this; | |
var logger = Logger.forTag('playlist-follow-popup', 'playlist-auto-follow-popup'); | |
if (this._popup) { | |
this._popup.dispose(); | |
} | |
var fragment = document.createDocumentFragment(); | |
var paragraph = document.createElement('p'); | |
paragraph.innerHTML = isAviciiPlaylist ? _('PopupAviciiAutoFollow') : _('PopupPlaylistSubscribeLine1', '<strong>' + this.item.owner.name.decodeForHtml() + '</strong>') + '<br><br>' + _('PopupPlaylistSubscribeLine2'); | |
fragment.appendChild(paragraph); | |
var controls = document.createElement('p'); | |
controls.className = 'sp-popup-buttons'; | |
fragment.appendChild(controls); | |
var unsubscribe = Button.withLabel(_('PopupPlaylistSubscribeCancel')); | |
controls.appendChild(unsubscribe.node); | |
var okay = Button.withLabel(_('PopupPlaylistSubscribeConfirm')); | |
okay.setAccentuated(true); | |
controls.appendChild(okay.node); | |
var popup = Popup.withContent(fragment, 250, 0, 'sp-playlist-subscribed'); | |
popup.addEventListener('hiddenOnClick', function () { | |
logger.userHit('hide', self._followLogData); | |
}); | |
var user = this.item.owner; | |
unsubscribe.addEventListener('click', function () { | |
this.setDisabled(true); | |
Relations.forCurrentUser().unsubscribe(user); | |
popup.hide(100); | |
logger.userHit('no', self._followLogData); | |
}); | |
okay.addEventListener('click', function () { | |
popup.hide(); | |
logger.userHit('ok', self._followLogData); | |
}); | |
Relations.forCurrentUser().subscribe(user).done(function () { | |
popup.showFor(self.node); | |
var rect = paragraph.getBoundingClientRect(); | |
popup.resize(popup.width, rect.height + 44); | |
logger.userImpression('playlist-follow-popup', self._followLogData); | |
}); | |
this._popup = popup; | |
}; | |
function updateFollowersCount(user, element) { | |
Relations.forUser(user).subscribers.snapshot(0, 0).done(function (snapshot) { | |
element.textContent = snapshot.length; | |
}); | |
} | |
var _followPopup = null; | |
SubscribePlaylistButton.prototype._showFollowPopup = function () { | |
var self = this; | |
var logger = Logger.forTag('playlist-follow-popup', 'playlist-suggest-follow-popup'); | |
if (_followPopup) { | |
logger.userHit('hide', this._followLogData); | |
_followPopup.dispose(); | |
} | |
var owner = this.item.owner, ownerName = owner.name.decodeForHtml(), ownerLink = owner.uri.toSpotifyLink(), discoverLink = 'spotify:app:discover'.toSpotifyLink(); | |
var fragment = document.createDocumentFragment(); | |
var paragraph = document.createElement('p'); | |
paragraph.innerHTML = _('PopupPlaylistSuggestFollow', '<a href="' + ownerLink + '"><strong>' + ownerName + '</strong></a>', ownerName, '<a href="' + discoverLink + '"><strong>' + _('DiscoverAppName') + '</strong></a>', '<strong>' + _('ButtonSubscribe') + '</strong>'); | |
fragment.appendChild(paragraph); | |
var container = document.createElement('div'); | |
container.className = 'sp-playlist-follow-profile-container'; | |
fragment.appendChild(container); | |
var content = document.createElement('div'); | |
content.className = 'sp-playlist-follow-profile-content'; | |
container.appendChild(content); | |
var portrait; | |
if (owner.image) { | |
portrait = document.createElement('img'); | |
portrait.src = owner.image; | |
} else { | |
portrait = document.createElement('div'); | |
var width = 45, height = 45, placeholderWidth = 578, placeholderHeight = 1500, bgWidthPercent = placeholderWidth / width * 100, bgHeightPercent = placeholderHeight / height * 100; | |
var placeholder = document.createElement('div'); | |
css.addClass(placeholder, 'sp-image-placeholder sp-image-placeholder-visible'); | |
placeholder.style.backgroundSize = bgWidthPercent + '% ' + bgHeightPercent + '%'; | |
portrait.appendChild(placeholder); | |
} | |
css.addClass(portrait, 'sp-profile-portrait'); | |
content.appendChild(portrait); | |
var nameContainer = document.createElement('div'); | |
nameContainer.className = 'sp-playlist-follow-profile-name'; | |
content.appendChild(nameContainer); | |
var nameLink = document.createElement('a'); | |
nameLink.href = ownerLink; | |
nameLink.innerHTML = '<strong>' + ownerName + '</strong>'; | |
nameContainer.appendChild(nameLink); | |
var info = document.createElement('div'); | |
info.className = 'sp-playlist-follow-profile-info'; | |
content.appendChild(info); | |
var followersCount = document.createElement('div'); | |
followersCount.className = 'sp-profile-followers-count'; | |
updateFollowersCount(owner, followersCount); | |
info.appendChild(followersCount); | |
var followersText = document.createElement('div'); | |
followersText.className = 'sp-profile-followers-text'; | |
followersText.textContent = _('Followers'); | |
info.appendChild(followersText); | |
var followButton = SubscribeButton.forUser(this.item.owner); | |
content.appendChild(followButton.node); | |
followButton.addEventListener('subscribe', function () { | |
updateFollowersCount(owner, followersCount); | |
logger.userHit('follow', self._followLogData); | |
}); | |
followButton.addEventListener('unsubscribe', function () { | |
updateFollowersCount(owner, followersCount); | |
logger.userHit('unfollow', self._followLogData); | |
}); | |
var popup = Popup.withContent(fragment, 250, 0, 'sp-playlist-follow'); | |
popup.showFor(this.node); | |
popup.addEventListener('hiddenOnClick', function () { | |
logger.userHit('hide', self._followLogData); | |
}); | |
var rect = paragraph.getBoundingClientRect(); | |
popup.resize(popup.width, rect.height + 60); | |
logger.userImpression('playlist-follow-popup', this._followLogData); | |
_followPopup = this._popup = popup; | |
}; | |
SubscribePlaylistButton.prototype._updateSubscription = function (subscribe) { | |
if (!subscribe) { | |
this._call(Library.forCurrentUser(), 'unsubscribe'); | |
if (this._popup) { | |
this._popup.hide(); | |
} | |
return; | |
} | |
var self = this; | |
models.session.testGroupForTest('playlist_follow_popup').done(function (testGroup) { | |
self.item.load('owner').done(function (playlist) { | |
playlist.owner.load('name', 'subscribed', 'image').done(function (user) { | |
self._followLogData = { | |
uri: playlist.uri, | |
follower: models.session.user.username, | |
owner: user.username | |
}; | |
self._call(Library.forCurrentUser(), 'subscribe').done(function () { | |
if (!user.subscribed) { | |
var isAviciiPlaylist = playlist.uri === 'spotify:user:aviciiofficial:playlist:0Nfd1i1ofRRIXN8Kyk5ms1'; | |
if (testGroup <= 50 || isAviciiPlaylist) { | |
self._showAutoFollowPopup(isAviciiPlaylist); | |
} else { | |
self._showFollowPopup(); | |
} | |
} | |
}); | |
}); | |
}); | |
}); | |
}; | |
function SubscribeProfileButton(profile, opt_options) { | |
var isProfile = profile instanceof models.Profile; | |
var isUser = profile instanceof models.User; | |
var isArtist = profile instanceof models.Artist; | |
if (!(isProfile || isUser || isArtist)) | |
throw new Error('Supplied object was not a Profile, Artist or User'); | |
SubscribeButton.call(this, profile, opt_options); | |
} | |
SP.inherit(SubscribeProfileButton, SubscribeButton); | |
SubscribeProfileButton.prototype._updateSubscription = function (subscribe) { | |
if (!this._itemLoaded) | |
return; | |
this._call(Relations.forCurrentUser(), subscribe ? 'subscribe' : 'unsubscribe'); | |
}; | |
SubscribeButton.forPlaylist = function (playlist, opt_options) { | |
if (!(playlist instanceof models.Playlist)) | |
throw new Error('not a Playlist'); | |
return new SubscribePlaylistButton(playlist, opt_options); | |
}; | |
SubscribeButton.forArtist = function (artist, opt_options) { | |
if (!(artist instanceof models.Artist)) | |
throw new Error('not an Artist'); | |
return new SubscribeProfileButton(artist, opt_options); | |
}; | |
SubscribeButton.forUser = function (user, opt_options) { | |
if (!(user instanceof models.User)) | |
throw new Error('not a valid User object'); | |
return new SubscribeProfileButton(user, opt_options); | |
}; | |
SubscribeButton.forProfile = function (profile, opt_options) { | |
var isProfile = profile instanceof models.Profile; | |
var isUser = profile instanceof models.User; | |
var isArtist = profile instanceof models.Artist; | |
if (!(isProfile || isUser || isArtist)) | |
throw new Error('Supplied object was not a Profile, Artist or User'); | |
return new SubscribeProfileButton(profile, opt_options); | |
}; | |
SubscribeButton.availableOptions = {}; | |
function QuickActionButtons(item, opt_origin, opt_indexGetter, opt_withContextButton) { | |
this.item = item; | |
this.origin = opt_origin; | |
this.indexGetter = opt_indexGetter; | |
var container = document.createElement('div'); | |
css.addClass(container, 'sp-quickactionbuttons'); | |
var deviceContainer = device.container; | |
if (opt_withContextButton === undefined) | |
opt_withContextButton = deviceContainer === 'web' || deviceContainer === 'ios'; | |
var contextMenu; | |
if (opt_withContextButton) { | |
css.addClass(container, 'sp-with-contextbutton'); | |
contextMenu = CustomButton.withClass('sp-contextmenu'); | |
container.appendChild(contextMenu.node); | |
contextMenu.addEventListener('click', SP.bind(this._clicked, this)); | |
} | |
if (deviceContainer !== 'ios') { | |
models.client.load('features').done(function (client) { | |
if (client.features.collection) { | |
css.addClass(container, 'sp-with-collection'); | |
var addToCollection = AddToCollectionButton.forItem(item, { autoAdjustSize: false }); | |
addToCollection.setAutoAccentuated(false); | |
container.insertBefore(addToCollection.node, contextMenu && contextMenu.node); | |
} | |
}); | |
} | |
this.node = container; | |
} | |
QuickActionButtons.forItem = function (item, opt_origin, opt_indexGetter, opt_withContextButton) { | |
return new QuickActionButtons(item, opt_origin, opt_indexGetter, opt_withContextButton); | |
}; | |
QuickActionButtons.prototype._clicked = function (e) { | |
var self = this; | |
var event = e.browserEvent || e; | |
var appClosedDetecter; | |
var moves = 0; | |
var index = undefined; | |
if (this.indexGetter) | |
index = this.indexGetter(event); | |
if (device.container === 'web') { | |
var origin = this.origin && this.origin.uri; | |
ContextApp.show('context-actions', [this.item.uri], event.target, origin, index, this._getLoggingContext()).done(function (contextApp) { | |
appClosedDetecter = function () { | |
moves++; | |
if (moves == 2) { | |
css.removeClass(self.node, 'sp-quickactionbuttons-popup'); | |
document.removeEventListener('mousemove', appClosedDetecter); | |
} | |
}; | |
document.addEventListener('mousemove', appClosedDetecter); | |
}); | |
} else { | |
var pointer = event.changedTouches ? event.changedTouches[0] : event; | |
var x = pointer.pageX - window.pageXOffset; | |
var y = pointer.pageY - window.pageYOffset; | |
models.client.showContextUI(this.item, { | |
x: x, | |
y: y | |
}, this.origin); | |
} | |
css.addClass(this.node, 'sp-quickactionbuttons-popup'); | |
}; | |
QuickActionButtons.prototype._getLoggingContext = function () { | |
var contextParts = []; | |
var contextPart; | |
var target = this.node; | |
if (target) { | |
do { | |
if (target && target.getAttribute && (contextPart = target.getAttribute('data-log-context'))) { | |
contextParts.push(contextPart); | |
} | |
} while (target = target.parentNode); | |
} | |
return contextParts.reverse().join('/'); | |
}; | |
function AddToCollectionButton(item, opt_options) { | |
var self = this; | |
Button.call(this, ''); | |
opt_options = opt_options || {}; | |
this._autoAdjustSize = true; | |
if (opt_options.autoAdjustSize === false) | |
this._autoAdjustSize = false; | |
this._setLabels(); | |
this.item = item; | |
this.node.setAttribute('data-uri', item.uri); | |
this._autoAccentuated = true; | |
css.addClass(this.node, 'sp-add-to-collection-button'); | |
this._library = Library.forCurrentUser(); | |
this.node.buttonInstance = this; | |
this._isInCollection = false; | |
this._justAdded = false; | |
dom.addEventListener(this.node, 'mouseover', function () { | |
self._isMouseHovering = true; | |
self._justAdded = false; | |
self._updateLooks(); | |
}); | |
dom.addEventListener(this.node, 'mouseout', function () { | |
self._isMouseHovering = false; | |
self._justAdded = false; | |
self._updateLooks(); | |
}); | |
this._updateLooks(); | |
AddToCollectionButton.updateStates(); | |
} | |
SP.inherit(AddToCollectionButton, Button); | |
AddToCollectionButton.forItem = function (item, opt_options) { | |
return new AddToCollectionButton(item, opt_options); | |
}; | |
AddToCollectionButton.availableOptions = {}; | |
AddToCollectionButton.prototype.setAutoAccentuated = function (autoAccentuated) { | |
this._autoAccentuated = autoAccentuated; | |
this._updateLooks(); | |
}; | |
AddToCollectionButton.prototype._setLabels = function () { | |
var _labels = this._collectionLabels = { | |
add: _('SaveToYourMusic'), | |
inCollection: _('SavedToYourMusic') | |
}; | |
var _icons = this._collectionIcons = { | |
add: true, | |
inCollection: false | |
}; | |
var labels = [ | |
_labels.add, | |
_labels.inCollection | |
]; | |
var icons = [ | |
_icons.add, | |
_icons.inCollection | |
]; | |
if (!this._touchDevice) { | |
_labels.remove = _('RemoveFromYourMusic'); | |
_icons.remove = false; | |
labels.push(_labels.remove); | |
icons.push(_icons.remove); | |
} | |
if (this._autoAdjustSize) | |
this.setWidthFromLabels(labels, icons); | |
}; | |
AddToCollectionButton.prototype._setInCollectionState = function (inCollection) { | |
if (this._isInCollection == inCollection) | |
return; | |
this._isInCollection = inCollection; | |
this._justAdded = this._isInCollection; | |
this._updateLooks(); | |
}; | |
AddToCollectionButton.prototype._clicked = function (e) { | |
var self = this; | |
models.client.load('features').done(function (client) { | |
var COLLECTION_WITH_UNION = 2; | |
if (self._isInCollection) { | |
if (self.item instanceof models.Track) | |
Logger.forTag('views').userHit('remove-track-from-collection', { data: { uri: self.item.uri } }, 'collection-button'); | |
else | |
Logger.forTag('views').userHit('remove-album-from-collection', { data: { uri: self.item.uri } }, 'collection-button'); | |
var onSuccess = function () { | |
}; | |
var removeItem = function () { | |
self._setInCollectionState(false); | |
if (self.item instanceof models.Track) | |
self._library.tracks.remove(self.item).fail(self._setInCollectionState.bind(self, true)); | |
else | |
self._library.albums.remove(self.item).fail(self._setInCollectionState.bind(self, true)); | |
}; | |
if (client.features.collection === COLLECTION_WITH_UNION) { | |
self._library.getUnionSources(self.item).done(function (sources) { | |
if (sources && sources.playlists && Object.keys(sources.playlists).length) { | |
SP.request('client_show_collection_union_remove_ui', [ | |
models.session.user.uri, | |
self.item.uri | |
], null, onSuccess, removeItem); | |
} else { | |
removeItem(); | |
} | |
}).fail(function () { | |
removeItem(); | |
}); | |
} else { | |
removeItem(); | |
} | |
} else { | |
self._setInCollectionState(true); | |
if (self.item instanceof models.Track) { | |
Logger.forTag('views').userHit('add-track-to-collection', { data: { uri: self.item.uri } }, 'collection-button'); | |
self._library.tracks.add(self.item).fail(self._setInCollectionState.bind(self, false)); | |
} else { | |
Logger.forTag('views').userHit('add-album-to-collection', { data: { uri: self.item.uri } }, 'collection-button'); | |
self._library.albums.add(self.item).fail(self._setInCollectionState.bind(self, false)); | |
} | |
} | |
}); | |
}; | |
AddToCollectionButton.prototype._updateLooks = function () { | |
if (this._justAdded) | |
css.addClass(this.node, 'sp-just-added-to-collection'); | |
else | |
css.removeClass(this.node, 'sp-just-added-to-collection'); | |
if (this._isInCollection) { | |
this.setIconClass(''); | |
css.addClass(this.node, 'sp-in-collection'); | |
css.removeClass(this.node, 'sp-button-accentuated-positive'); | |
if (this._isMouseHovering) { | |
this.setLabel(this._collectionLabels.remove); | |
css.addClass(this.node, 'sp-button-accentuated-negative'); | |
css.addClass(this.node, 'sp-button-accentuated'); | |
} else { | |
this.setLabel(this._collectionLabels.inCollection); | |
css.removeClass(this.node, 'sp-button-accentuated-negative'); | |
css.removeClass(this.node, 'sp-button-accentuated'); | |
} | |
} else { | |
this.setIconClass('sp-icon-add'); | |
this.setLabel(this._collectionLabels.add); | |
css.removeClass(this.node, 'sp-in-collection'); | |
css.removeClass(this.node, 'sp-button-accentuated-negative'); | |
if (this._autoAccentuated) { | |
css.addClass(this.node, 'sp-button-accentuated-positive'); | |
css.addClass(this.node, 'sp-button-accentuated'); | |
} else { | |
css.removeClass(this.node, 'sp-button-accentuated-positive'); | |
css.removeClass(this.node, 'sp-button-accentuated'); | |
} | |
} | |
}; | |
AddToCollectionButton._updateSubsetStates = function (buttonInstances, modelConstructor, collection) { | |
var buttonInstancesSubset = []; | |
var items = []; | |
for (var i = 0; i < buttonInstances.length; i++) { | |
var item = buttonInstances[i].item; | |
if (item instanceof modelConstructor) { | |
buttonInstancesSubset.push(buttonInstances[i]); | |
items.push(item); | |
} | |
} | |
collection.contains(items).done(function (isInCollection) { | |
for (var i = 0; i < items.length; i++) { | |
buttonInstancesSubset[i]._setInCollectionState(isInCollection[i]); | |
} | |
}); | |
}; | |
AddToCollectionButton._updateStates = function () { | |
var library = Library.forCurrentUser(); | |
var buttons = AddToCollectionButton._getAllMatching(); | |
AddToCollectionButton._updateSubsetStates(buttons, models.Track, library.tracks); | |
AddToCollectionButton._updateSubsetStates(buttons, models.Album, library.albums); | |
}; | |
AddToCollectionButton.updateStates = function () { | |
if (AddToCollectionButton._timer) | |
clearTimeout(AddToCollectionButton._timer); | |
AddToCollectionButton._timer = setTimeout(AddToCollectionButton._updateStates, 10); | |
}; | |
AddToCollectionButton._initGlobally = function () { | |
var library = Library.forCurrentUser(); | |
library.tracks.addEventListener('changed', AddToCollectionButton._handleChangedEvent); | |
library.albums.addEventListener('changed', AddToCollectionButton._handleChangedEvent); | |
library.tracks.addEventListener('insert', AddToCollectionButton._handleInsertRemoveEvent); | |
library.tracks.addEventListener('remove', AddToCollectionButton._handleInsertRemoveEvent); | |
library.albums.addEventListener('insert', AddToCollectionButton._handleInsertRemoveEvent); | |
library.albums.addEventListener('remove', AddToCollectionButton._handleInsertRemoveEvent); | |
}; | |
AddToCollectionButton._handleChangedEvent = function (e) { | |
AddToCollectionButton.updateStates(); | |
}; | |
AddToCollectionButton._handleInsertRemoveEvent = function (e) { | |
var isInCollection = e.type === 'insert'; | |
e.uris.forEach(function (uri) { | |
AddToCollectionButton._getAllMatching(uri).forEach(function (button) { | |
button._setInCollectionState(isInCollection); | |
}); | |
}); | |
}; | |
AddToCollectionButton._getAllMatching = function (opt_uri) { | |
var selector = '.sp-add-to-collection-button' + (opt_uri ? '[data-uri="' + opt_uri + '"]' : ''); | |
return Array.prototype.map.call(document.querySelectorAll(selector), function (button) { | |
return button.buttonInstance; | |
}); | |
}; | |
AddToCollectionButton._initGlobally(); | |
}(require('node_modules/views/scripts/utils/device.js'), require('node_modules/api/scripts/library.js').Library, require('node_modules/api/scripts/models.js'), require('node_modules/api/scripts/toplists.js').Toplist, require('node_modules/api/scripts/relations.js').Relations, require('node_modules/views/scripts/popup.js').Popup, require('node_modules/views/@loc.loc/strings/buttons.lang'), require('node_modules/views/scripts/utils/css.js'), require('node_modules/views/scripts/utils/dom.js'), require('node_modules/views/scripts/utils/dnd.js'), require('node_modules/views/scripts/utils/touch.js'), require('node_modules/views/scripts/contextapp.js').ContextApp, require('node_modules/views/scripts/utils/frame.js'), require('node_modules/views/scripts/utils/logger.js').Logger)); | |
}, | |
'node_modules/views/scripts/image.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (imageStrings, device, models, Toplist, css, dom, dnd, touch, buttons) { | |
var _ = SP.bind(imageStrings.get, imageStrings); | |
exports.Image = Image; | |
var Observable = models.Observable; | |
function Image(item, options) { | |
options = options || {}; | |
this._title = options.title; | |
this._link = options.link || ''; | |
this._animateLoaded = options.animate === undefined ? true : options.animate; | |
this._placeholder = options.placeholder === undefined ? 'auto' : options.placeholder; | |
this._player = !!options.player; | |
this._quickActionMenu = options.quickActionMenu === undefined ? 'auto' : options.quickActionMenu; | |
this._playerItem = options.playerItem; | |
this._swap = options.swap === 'immediate' ? 'immediate' : 'wait'; | |
this._overlay = options.overlay === undefined ? [] : options.overlay; | |
this._doAutoOverlay = typeof options.overlay === 'boolean'; | |
this._playerCentered = options.playerCentered || false; | |
var styles = [ | |
'plain', | |
'inset', | |
'rounded', | |
'embossed' | |
]; | |
this._style = ~styles.indexOf(options.style) ? options.style : 'inset'; | |
this._getContextGroupData = options.getContextGroupData; | |
this._getIndexInContext = options.getIndexInContext; | |
this._acceptedLoadTime = 200; | |
this._width = options.width || options.height || 200; | |
this._height = options.height || options.width || 200; | |
var placeholderTypes = { | |
'artist': 'Artist', | |
'album': 'Album', | |
'track': 'Track', | |
'playlist': 'Playlist', | |
'user': 'User' | |
}; | |
if (!~' auto none empty '.indexOf(' ' + this._placeholder + ' ') && !(this._placeholder in placeholderTypes)) { | |
this._placeholder = 'auto'; | |
} | |
this._placeholderType = placeholderTypes[this._placeholder] || 'auto'; | |
this._buildNode(); | |
this._isCustomImage = item ? typeof item === 'string' ? true : false : true; | |
if (this._placeholder !== 'none') { | |
this._buildPlaceholder(); | |
} | |
if (item !== undefined) { | |
this.setImage(item); | |
} else if (this._placeholder !== 'none') { | |
this._setPlaceholder(this._getSuitableSize('placeholder')); | |
} | |
if (this._link) { | |
this.setLink(this._link); | |
} | |
if (dnd.drag.hasDragSupport) { | |
this._addDragHandler(); | |
} | |
if (device.container === 'desktop' || device.container === 'web') { | |
this._addContextUIHandler(); | |
} | |
} | |
SP.inherit(Image, Observable); | |
Image.forAlbum = function (album, opt_options) { | |
if (!(album instanceof models.Album)) { | |
throw new Error('The type of the object is not Album'); | |
} | |
return new Image(album, opt_options); | |
}; | |
Image.forArtist = function (artist, opt_options) { | |
if (!(artist instanceof models.Artist)) { | |
throw new Error('The type of the object is not Artist'); | |
} | |
return new Image(artist, opt_options); | |
}; | |
Image.forPlaylist = function (playlist, opt_options) { | |
if (!(playlist instanceof models.Playlist)) { | |
throw new Error('The type of the object is not Playlist'); | |
} | |
return new Image(playlist, opt_options); | |
}; | |
Image.forTrack = function (track, opt_options) { | |
if (!(track instanceof models.Track)) { | |
throw new Error('The type of the object is not Track'); | |
} | |
return new Image(track, opt_options); | |
}; | |
Image.forUser = function (user, opt_options) { | |
if (!(user instanceof models.User)) { | |
throw new Error('The type of the object is not User'); | |
} | |
return new Image(user, opt_options); | |
}; | |
Image.forProfile = function (profile, opt_options) { | |
if (!(profile instanceof models.Profile)) { | |
throw new Error('The type of the object is not Profile'); | |
} | |
return new Image(profile, opt_options); | |
}; | |
Image.fromSource = function (source, opt_options) { | |
if (typeof source !== 'string') { | |
throw new Error('The source path you pass in must be a string.'); | |
} | |
return new Image(source, opt_options); | |
}; | |
Image.availableOptions = {}; | |
Image.prototype.setImage = function (item) { | |
var self = this; | |
this.isLoaded = false; | |
this._isCustomImage = typeof item === 'string'; | |
if (this._isCustomImage) { | |
this._item = null; | |
this._src = item; | |
} else { | |
var isOldTrack = this._item instanceof models.Track; | |
var isOldPlaylist = this._item instanceof models.Playlist; | |
if (this._item && (isOldTrack || isOldPlaylist)) { | |
this._item.removeEventListener('change:image', this._changeEventHandler); | |
this._changeEventHandler = null; | |
} | |
this._src = null; | |
this._item = item; | |
var isNewTrack = item instanceof models.Track; | |
var isNewPlaylist = item instanceof models.Playlist; | |
if (isNewTrack || isNewPlaylist) { | |
item.load('image').done(function () { | |
self._changeEventHandler = function () { | |
self._resetImage(); | |
self._buildImage(); | |
}; | |
item.addEventListener('change:image', self._changeEventHandler); | |
}); | |
} | |
} | |
if (this._placeholder !== 'none') { | |
this._setPlaceholder(this._getSuitableSize('placeholder')); | |
} | |
if (this._swap === 'immediate') { | |
this._resetImage(); | |
} | |
this.setDraggable(); | |
this._buildImage(); | |
if (this.playerButton) { | |
if (this._isCustomImage && !this._playerItem || !this._isSupportedPlayerItem()) { | |
this._hidePlayButton(); | |
} else { | |
this._showPlayButton(); | |
if (this._isCustomImage) { | |
this.playerButton.setItem(this._playerItem); | |
} else { | |
this.playerButton.setItem(item); | |
} | |
} | |
} else if (this._player && this._isSupportedPlayerItem()) { | |
this._buildPlayer(); | |
} | |
if (this._doAutoOverlay) | |
this._setAutoOverlay(); | |
else if (!this.node.overlay && this._overlay.length > 0) | |
this._applyOverlay(this._overlay[0], this._overlay[1]); | |
var typeIsTrackOrAlbum = this._item instanceof models.Track || this._item instanceof models.Album; | |
var okForAutoQuickActionMenu = typeIsTrackOrAlbum && this._getSize().width > 64; | |
if (this._quickActionMenu === true || this._quickActionMenu == 'auto' && okForAutoQuickActionMenu) { | |
var builtButtons = false; | |
models.client.load('features').done(function (client) { | |
if (!builtButtons && client.features.collection) { | |
builtButtons = true; | |
self._buildQuickActionButtons(item); | |
} | |
}); | |
if (!builtButtons && device.container === 'web') { | |
builtButtons = true; | |
this._buildQuickActionButtons(item); | |
} | |
} | |
return this; | |
}; | |
Image.prototype.setOverlay = function (opt_firstLineOrDoAutomatic, opt_secondLine) { | |
if (!opt_firstLineOrDoAutomatic && !opt_secondLine) { | |
this._clearOverlay(); | |
return; | |
} | |
this._doAutoOverlay = opt_firstLineOrDoAutomatic === true; | |
if (this._doAutoOverlay) | |
this._setAutoOverlay(); | |
else | |
this._applyOverlay(opt_firstLineOrDoAutomatic, opt_secondLine); | |
}; | |
Image.prototype._clearOverlay = function () { | |
this._overlay = []; | |
if (this.node.overlay) { | |
this.node.removeChild(this.node.overlay); | |
delete this.node.overlay; | |
} | |
if (this.playerButton && !this._playerCentered) { | |
this.playerButton.setCentered(false); | |
} | |
}; | |
Image.prototype._applyOverlay = function (opt_firstLine, opt_secondLine) { | |
this._overlay = []; | |
this._overlay[0] = opt_firstLine || ''; | |
if (opt_secondLine) { | |
this._overlay[1] = opt_secondLine; | |
} else { | |
this._overlay.splice(1, 1); | |
} | |
if (!this.node.overlay) { | |
this._buildOverlay(); | |
} | |
var lineNames = [ | |
'firstLine', | |
'secondLine' | |
]; | |
for (var i = 0, len = lineNames.length; i < len; i++) { | |
var line = this._overlay[i] || ''; | |
var lineName = lineNames[i]; | |
var lineNode = this.node.overlay[lineName]; | |
if (lineNode) { | |
if (line instanceof HTMLElement) { | |
while (lineNode.firstChild) { | |
lineNode.removeChild(lineNode.firstChild); | |
} | |
lineNode.appendChild(line); | |
} else { | |
lineNode.textContent = line; | |
} | |
} | |
} | |
if (this._overlay.length > 1) { | |
css.addClass(this.node.overlay, 'sp-image-overlay-2-lines'); | |
} else { | |
css.removeClass(this.node.overlay, 'sp-image-overlay-2-lines'); | |
} | |
if (this.playerButton) { | |
this.playerButton.setCentered(true); | |
} | |
}; | |
var createLink = function (uri, text) { | |
var link = document.createElement('a'); | |
link.href = uri.toSpotifyLink(); | |
link.textContent = text; | |
return link; | |
}; | |
var getArtists = function (artists, opt_cb) { | |
var i = 0, l = artists.length, promises = [], _this = this; | |
for (; i < l; i++) { | |
promises.push(artists[i].load('name', 'uri')); | |
} | |
models.Promise.join(promises).done(function (artists) { | |
var wrapper = document.createElement('span'); | |
var i = 0, l = artists.length; | |
for (; i < l; i++) { | |
wrapper.appendChild(createLink(artists[i].uri, artists[i].name)); | |
if (i < l - 1) { | |
wrapper.appendChild(document.createTextNode(', ')); | |
} | |
} | |
if (opt_cb && typeof opt_cb === 'function') { | |
opt_cb(wrapper); | |
} | |
}); | |
}; | |
Image.prototype._setAutoOverlay = function () { | |
var firstline = null, _this = this; | |
if (this._item instanceof models.Playlist) { | |
this._item.load('name', 'owner').done(function (playlist) { | |
firstline = createLink(_this._item.uri, playlist.name); | |
playlist.owner.load('name', 'uri').done(function (user) { | |
_this._applyOverlay(firstline, createLink(user.uri, _('by') + ' ' + user.name)); | |
}); | |
}); | |
} else if (this._item instanceof models.Album) { | |
this._item.load('name', 'artists').done(function (album) { | |
firstline = createLink(_this._item.uri, album.name); | |
getArtists(album.artists, function (artists) { | |
_this._applyOverlay(firstline, artists); | |
}); | |
}); | |
} else if (this._item instanceof models.Track) { | |
this._item.load('name', 'artists').done(function (track) { | |
firstline = createLink(_this._item.uri, track.name); | |
getArtists(track.artists, function (artists) { | |
_this._applyOverlay(firstline, artists); | |
}); | |
}); | |
} else if (this._item instanceof models.User) { | |
this._item.load('name').done(function (user) { | |
if (user.name) { | |
_this._applyOverlay(createLink(_this._item.uri, user.name)); | |
} | |
}); | |
} else if (this._item instanceof models.Artist) { | |
this._item.load('name').done(function (artist) { | |
if (artist.name) { | |
_this._applyOverlay(createLink(_this._item.uri, artist.name)); | |
} | |
}); | |
} else if (this._item instanceof models.Profile) { | |
this._item.load('name').done(function (profile) { | |
if (profile.name) { | |
_this._applyOverlay(createLink(_this._item.uri, profile.name)); | |
} | |
}); | |
} | |
}; | |
Image.prototype.setLink = function (link) { | |
var node = this.node; | |
this._link = link || ''; | |
if (this._link === 'auto' && !this._isCustomImage) { | |
if (this._item && this._item.uri) { | |
node.href = this._item.uri.toSpotifyLink(); | |
node.setAttribute('data-uri', this._item.uri); | |
} else { | |
node.href = '#'; | |
node.setAttribute('data-uri', ''); | |
} | |
} else { | |
var isSpotifyURI = this._link.indexOf('spotify:') === 0; | |
var link = isSpotifyURI ? this._link.toSpotifyLink() : this._link; | |
node.href = link; | |
node.setAttribute('data-uri', isSpotifyURI ? this._link : ''); | |
} | |
}; | |
Image.prototype.setSize = function (width, height) { | |
this.node.style.width = width + 'px'; | |
this.node.style.height = height + 'px'; | |
this._width = width; | |
this._height = height; | |
if (this._placeholder !== 'none' && this.node.placeholder) { | |
this._setPlaceholder(this._getSuitableSize('placeholder')); | |
} | |
if (this.isImageInitialized && this._player) { | |
this._setPlayButtonSize(); | |
} | |
this.dispatchEvent('resize'); | |
return this; | |
}; | |
Image.prototype.setStyle = function (style) { | |
if (this.isImageInitialized && style === this._style) | |
return this; | |
var stylesWithInsetEl = [ | |
'inset', | |
'embossed' | |
]; | |
css.removeClass(this.node, 'sp-image-style-' + this._style); | |
var inset_shadow = this.node.getElementsByClassName('sp-image-inset')[0]; | |
if (inset_shadow && !~stylesWithInsetEl.indexOf(style)) { | |
this.node.removeChild(inset_shadow); | |
css.removeClass(this.node, 'sp-image-style-rounded'); | |
} | |
if (!inset_shadow && ~stylesWithInsetEl.indexOf(style)) { | |
css.addClass(this.node, 'sp-image-style-rounded'); | |
var inset = document.createElement('div'); | |
css.addClass(inset, 'sp-image-inset'); | |
this.node.appendChild(inset); | |
} | |
css.addClass(this.node, 'sp-image-style-' + style); | |
this._style = style; | |
return this; | |
}; | |
Image.prototype.setPlayer = function (doSetPlayer) { | |
if (doSetPlayer && !this._player) { | |
this._buildPlayer(); | |
} else if (!doSetPlayer && this._player) { | |
this._removePlayer(); | |
} | |
this._player = !!doSetPlayer; | |
return this; | |
}; | |
Image.prototype.setDraggable = function (opt_doActivate) { | |
var doActivate = opt_doActivate !== undefined ? opt_doActivate : !this._isCustomImage || !!this._playerItem; | |
if (doActivate) { | |
this.node.setAttribute('draggable', 'true'); | |
css.removeClass(this.node, 'sp-image-disable-dnd'); | |
} else { | |
this.node.setAttribute('draggable', 'false'); | |
css.addClass(this.node, 'sp-image-disable-dnd'); | |
} | |
}; | |
Image.prototype._buildNode = function () { | |
var node; | |
var self = this; | |
if (this._link) { | |
this.node = node = document.createElement('a'); | |
this.setLink(this._link); | |
} else { | |
this.node = node = document.createElement('div'); | |
} | |
this._addClickHandler(); | |
if (this._title) { | |
node.title = this._title; | |
} | |
this.setSize(this._width, this._height); | |
css.addClass(node, 'sp-image'); | |
if (this._animateLoaded) { | |
css.addClass(node, 'sp-image-animated'); | |
} | |
this.setStyle(this._style); | |
css.addClass(node, 'sp-image-hidden'); | |
this.isImageInitialized = true; | |
return this; | |
}; | |
Image.prototype._addClickHandler = function () { | |
var self = this; | |
var node = this.node; | |
var hasTouch = device.touch && device.mobile; | |
var clickEvent = 'click'; | |
if (hasTouch) { | |
clickEvent = 'sp-tap'; | |
touch.selection.enableForElement(node, { willOpenNewView: true }); | |
node.addEventListener('click', function (e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
}); | |
} | |
node.addEventListener(clickEvent, function (e) { | |
self._onClick(e); | |
}); | |
}; | |
Image.prototype._onClick = function (e) { | |
var self = this; | |
var target = e.target; | |
if (e.type === 'sp-tap' && e.detail && e.detail.touchEndObject) { | |
target = e.detail.touchEndObject.target; | |
} | |
if (this.playerButton && this.playerButton.contains(target)) { | |
return; | |
} | |
do { | |
if (target.nodeName.toLowerCase() === 'a') { | |
break; | |
} | |
} while ((target = target.parentNode) && target !== document.body); | |
var uri = target.getAttribute('data-uri'); | |
if (!uri) { | |
var href = target.getAttribute('href'); | |
if (!href) | |
return; | |
uri = href.toSpotifyURI(); | |
} | |
if (uri && uri.indexOf('spotify:') === 0) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
models.application.openURI(uri); | |
self.dispatchEvent({ | |
type: 'link-click', | |
uri: uri, | |
targetElement: target | |
}); | |
} | |
}; | |
var placeHolderStyleCache = null; | |
var getPlaceHolderStyle = function () { | |
if (placeHolderStyleCache) { | |
return placeHolderStyleCache; | |
} | |
var dummy = document.createElement('div'); | |
css.addClass(dummy, 'sp-image-placeholder-visible'); | |
dummy.style.position = 'absolute'; | |
dummy.style.top = '-9999px'; | |
document.body.appendChild(dummy); | |
placeHolderStyleCache = /url\("?(.*?)"?\)/.exec(css.getStyle(dummy, 'background-image')); | |
document.body.removeChild(dummy); | |
return placeHolderStyleCache; | |
}; | |
Image.prototype._buildPlaceholder = function () { | |
var style = getPlaceHolderStyle(); | |
var node = document.createElement('div'); | |
css.addClass(node, 'sp-image-placeholder'); | |
if (style && typeof style[1] === 'string') { | |
var ph = this._placeholder; | |
var isCustom = this._isCustomImage; | |
if (ph !== 'empty' && !isCustom || isCustom && ph !== 'auto' && ph !== 'empty') { | |
css.addClass(node, 'sp-image-placeholder-visible'); | |
} | |
} | |
this.node.appendChild(node); | |
this.node.placeholder = node; | |
css.addClass(node, 'sp-image-placeholder-hidden'); | |
}; | |
Image.prototype._buildPlayer = function () { | |
var self = this; | |
var item = this._playerItem || this._item; | |
if (!item) | |
return; | |
var options = {}; | |
if (typeof this._getContextGroupData === 'function') { | |
options.getContextGroupData = this._getContextGroupData; | |
} | |
if (typeof this._getIndexInContext === 'function') { | |
options.getIndexInContext = this._getIndexInContext; | |
} | |
if (this._playerCentered) { | |
options.position = 'centered'; | |
} | |
var button = buttons.PlayButton.forItem(item, options); | |
this.node.appendChild(button.node); | |
css.addClass(button.node, 'sp-image-player'); | |
this.playerButton = button; | |
if (!this._isSupportedPlayerItem()) { | |
this._hidePlayButton(); | |
} | |
this._playClickHandler = function () { | |
self.dispatchEvent('play-click'); | |
}; | |
this._pauseClickHandler = function () { | |
self.dispatchEvent('pause-click'); | |
}; | |
button.addEventListener('play-click', this._playClickHandler); | |
button.addEventListener('pause-click', this._pauseClickHandler); | |
if (!this.hasOwnProperty('isPlaying')) { | |
Object.defineProperty(this, 'isPlaying', { | |
get: function () { | |
return this.playerButton ? this.playerButton._playing : false; | |
} | |
}); | |
} | |
this._playButtonSizes = { | |
40: 'xs', | |
64: 'small', | |
128: 'medium', | |
200: 'large', | |
300: 'xl' | |
}; | |
this._setPlayButtonSize(); | |
dom.addEventListener(this.playerButton.node, 'click', function (e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
}); | |
}; | |
Image.prototype._removePlayer = function () { | |
var playerButton = this.playerButton; | |
var node = playerButton && playerButton.node; | |
if (node && node.parentNode) | |
node.parentNode.removeChild(node); | |
if (playerButton) { | |
playerButton.removeEventListener('play-click', this._playClickHandler); | |
playerButton.removeEventListener('pause-click', this._pauseClickHandler); | |
delete this._playClickHandler; | |
delete this._pauseClickHandler; | |
} | |
this.playerButton = null; | |
}; | |
Image.prototype._isSupportedPlayerItem = function (opt_item) { | |
var item = opt_item || this._playerItem || this._item; | |
var isAlbum = item instanceof models.Album; | |
var isArtist = item instanceof models.Artist; | |
var isTrack = item instanceof models.Track; | |
var isPlaylist = item instanceof models.Playlist; | |
return isAlbum || isArtist || isTrack || isPlaylist; | |
}; | |
Image.prototype._showPlayButton = function () { | |
css.removeClass(this.playerButton.node, 'sp-image-player-hidden'); | |
}; | |
Image.prototype._hidePlayButton = function () { | |
css.addClass(this.playerButton.node, 'sp-image-player-hidden'); | |
}; | |
Image.prototype._buildOverlay = function () { | |
var container = document.createElement('div'); | |
css.addClass(container, 'sp-image-overlay'); | |
var firstLine = document.createElement('p'); | |
firstLine.className = 'sp-image-overlay-line1'; | |
container.appendChild(firstLine); | |
var secondLine = document.createElement('p'); | |
secondLine.className = 'sp-image-overlay-line2'; | |
container.appendChild(secondLine); | |
this.node.appendChild(container); | |
this.node.overlay = container; | |
this.node.overlay.firstLine = firstLine; | |
this.node.overlay.secondLine = secondLine; | |
}; | |
Image.prototype._buildQuickActionButtons = function (item) { | |
var quickActionButtons = buttons.QuickActionButtons.forItem(item); | |
dom.addEventListener(quickActionButtons.node, 'click', function (e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
}); | |
this.node.appendChild(quickActionButtons.node); | |
}; | |
Image.prototype._resetImage = function () { | |
if (this.node.wrapper) { | |
this.node.wrapper.innerHTML = ''; | |
} | |
css.removeClass(this.node, 'sp-image-loaded'); | |
css.removeClass(this.node, 'sp-image-hidden'); | |
if (this.node.placeholder) { | |
css.removeClass(this.node.placeholder, 'sp-image-placeholder-hidden'); | |
} | |
this.dispatchEvent('reset'); | |
}; | |
Image.prototype._setPlayButtonSize = function () { | |
if (!this.playerButton) | |
return; | |
var size; | |
if (device.mobile) { | |
size = 'medium'; | |
} else { | |
var lastWidth, width; | |
for (width in this._playButtonSizes) { | |
if (width > this._width) { | |
break; | |
} | |
lastWidth = width; | |
} | |
if (lastWidth) { | |
size = this._playButtonSizes[lastWidth]; | |
} | |
} | |
var isTrack = (this._playerItem || this._item) instanceof models.Track; | |
if (!size || size === 'xs' && isTrack) { | |
this._hidePlayButton(); | |
return; | |
} | |
this.playerButton.setSize(size); | |
if (this._isSupportedPlayerItem()) { | |
this._showPlayButton(); | |
} | |
var enableCentered = this._overlay.length > 0 || this._playerCentered; | |
this.playerButton.setCentered(enableCentered); | |
}; | |
Image.prototype.play = function () { | |
if (!this.playerButton) | |
return; | |
this.playerButton._play(); | |
}; | |
Image.prototype.pause = function () { | |
if (!this.playerButton) | |
return; | |
this.playerButton._pause(); | |
}; | |
Image.prototype._buildImage = function () { | |
if (this._isCustomImage) { | |
this._loadingStarted(); | |
this._createImage(this._src); | |
if (this._playerItem) { | |
var self = this; | |
this._playerItem.load('name').done(function (item) { | |
var name = item.name || function () { | |
if (item.uri.indexOf(':starred') > -1) { | |
return _('Starred'); | |
} else if (item.uri.indexOf(':toplist') > -1) { | |
return _('Toplist'); | |
} | |
}(); | |
if (name) { | |
self.node.setAttribute('data-tooltip', name); | |
} else { | |
self.setDraggable(false); | |
} | |
}); | |
} | |
} else { | |
var props; | |
if (this._item instanceof models.Album || this._item instanceof models.Track) { | |
props = [ | |
'image', | |
'name', | |
'artists' | |
]; | |
} else { | |
props = [ | |
'image', | |
'name' | |
]; | |
} | |
this._loadingStarted(); | |
this._item.load(props).done(this, function (item) { | |
var size = Math.max(this._width, this._height); | |
var image = item.imageForSize(size); | |
if (image) { | |
this._createImage(image); | |
} else { | |
this._resetImage(); | |
} | |
if (item.artists) { | |
var promises = []; | |
for (var i = 0, l = item.artists.length; i < l; i++) { | |
promises.push(item.artists[i].load('name')); | |
} | |
models.Promise.join(promises).done(this, function (artists) { | |
this.node.setAttribute('data-tooltip', item.name + ' by ' + this._getArtistsAsString(artists)); | |
}); | |
} else { | |
this.node.setAttribute('data-tooltip', item.name); | |
} | |
this.node.setAttribute('data-uri', item.uri); | |
}).fail(this, function () { | |
this._resetImage(); | |
}); | |
} | |
}; | |
Image.prototype._loadingStarted = function () { | |
var self = this; | |
setTimeout(function () { | |
if (!self.isLoaded) { | |
css.removeClass(self.node, 'sp-image-hidden'); | |
if (self.node.placeholder) { | |
css.removeClass(self.node.placeholder, 'sp-image-placeholder-hidden'); | |
} | |
} | |
}, this._acceptedLoadTime); | |
this._startLoadTime = +new Date(); | |
}; | |
Image.prototype._createImage = function (src) { | |
var self = this; | |
var dummyImg = document.createElement('img'); | |
var img = document.createElement('div'); | |
css.addClass(img, 'sp-image-img'); | |
dummyImg.src = src; | |
dummyImg.onload = function () { | |
self._onLoad(img); | |
}; | |
dummyImg.onerror = function () { | |
self._resetImage(); | |
}; | |
img.style.backgroundImage = 'url(' + src + ')'; | |
}; | |
Image.prototype._artificialLoading = function (img) { | |
var timeDiff = +new Date() - this._startLoadTime; | |
var artificialLoadingTime = 500; | |
var low = this._acceptedLoadTime; | |
var high = low + artificialLoadingTime; | |
if (timeDiff >= low && timeDiff <= high) { | |
var self = this; | |
setTimeout(function () { | |
self._startLoadTime = +new Date(); | |
self._onLoad(img); | |
}, artificialLoadingTime); | |
return true; | |
} | |
return false; | |
}; | |
Image.prototype._onLoad = function (img) { | |
if (this._artificialLoading(img)) | |
return; | |
var killOld = false; | |
if (this.hasBuiltOnce && this._swap === 'wait') { | |
css.addClass(this.node.wrapper, 'sp-image-wrapper-waiting-kill'); | |
css.removeClass(this.node, 'sp-image-loaded'); | |
killOld = true; | |
var oldWrapper = this.node.wrapper; | |
} | |
var wrapper = document.createElement('div'); | |
css.addClass(wrapper, 'sp-image-wrapper'); | |
wrapper.appendChild(img); | |
var playerButtnNode; | |
if (this.playerButton) | |
playerButtnNode = this.playerButton.node; | |
var refElem = this.node.wrapper || this.node.placeholder || playerButtnNode; | |
if (refElem) { | |
this.node.insertBefore(wrapper, refElem); | |
} else { | |
this.node.appendChild(wrapper); | |
} | |
this.node.wrapper = wrapper; | |
if (this.hasBuiltOnce && this._swap === 'wait') { | |
css.addClass(wrapper, 'sp-image-wrapper-waiting'); | |
} | |
this.hasBuiltOnce = true; | |
this.isLoaded = true; | |
css.removeClass(this.node, 'sp-image-hidden'); | |
SP.defer(this, function () { | |
css.addClass(this.node, 'sp-image-loaded'); | |
if (this._link) { | |
this.setLink(this._link); | |
} | |
this.dispatchEvent('load'); | |
this.dispatchEvent('change'); | |
if (killOld) { | |
var self = this; | |
setTimeout(function () { | |
oldWrapper.parentNode.removeChild(oldWrapper); | |
if (self._swap === 'wait') { | |
css.removeClass(wrapper, 'sp-image-wrapper-waiting'); | |
} | |
if (self.node.placeholder) { | |
css.removeClass(self.node.placeholder, 'sp-image-placeholder-hidden'); | |
} | |
}, this._animateLoaded ? 150 : 1); | |
} | |
}); | |
}; | |
Image.prototype._getSize = function () { | |
return { | |
width: this._width, | |
height: this._height | |
}; | |
}; | |
Image.prototype._getSuitableSize = function (type) { | |
var imageSize = this._getSize(); | |
var sizeKey, size, placeholderSize; | |
if (type === 'placeholder') { | |
if (this._placeholder === 'empty' || this._isCustomImage && this._placeholder === 'auto') { | |
return 'empty'; | |
} | |
var images = SIZES.placeholder.images; | |
for (sizeKey in images) { | |
size = images[sizeKey]; | |
if (size.width < imageSize.width || size.height < imageSize.height || size.width === undefined) { | |
break; | |
} | |
placeholderSize = sizeKey; | |
} | |
if (placeholderSize) { | |
return placeholderSize; | |
} else { | |
return sizeKey; | |
} | |
} | |
}; | |
Image.prototype._setPlaceholder = function (size) { | |
var imageSize = this._getSize(); | |
var placeholder = this.node.placeholder; | |
if (size === 'empty' || this._placeholder === 'auto' && !this._item && !this._src) { | |
css.removeClass(placeholder, 'sp-image-placeholder-visible'); | |
} else if (this._item || this._placeholder !== 'auto') { | |
css.addClass(placeholder, 'sp-image-placeholder-visible'); | |
size = SIZES.placeholder.images[size]; | |
if (size) { | |
var total, factorX, factorY, widthOfResized, newPercentage, leftOfResized, topOfResized, newLeftPercentage, newTopPercentage, itemType, typeOffset, offsetOfResized, newOffsetPercentage; | |
total = SIZES.placeholder.total; | |
factorX = imageSize.width / size.width; | |
factorY = imageSize.height / size.height; | |
newPercentage = factorX * total.width / imageSize.width * 100; | |
newLeftPercentage = factorX * size.x / (total.width * factorX - imageSize.width) * 100; | |
newTopPercentage = factorY * size.y / (total.height * factorY - imageSize.height) * 100; | |
var itemName; | |
if (this._item instanceof models.Album) { | |
itemName = 'Album'; | |
} else if (this._item instanceof models.Artist) { | |
itemName = 'Artist'; | |
} else if (this._item instanceof models.Playlist) { | |
itemName = 'Playlist'; | |
} else if (this._item instanceof models.Profile) { | |
itemName = 'User'; | |
} else if (this._item instanceof models.Track) { | |
itemName = 'Track'; | |
} else if (this._item instanceof models.User) { | |
itemName = 'User'; | |
} | |
itemType = this._placeholder === 'auto' ? itemName : this._placeholderType; | |
typeOffset = total.height / total.numTypes * SIZES.placeholder.offsets[itemType]; | |
offsetOfResized = typeOffset * factorY; | |
newTopPercentage += offsetOfResized / (total.height * factorY - imageSize.height) * 100; | |
var placeholderRatio = total.height / total.width; | |
placeholder.style.backgroundSize = newPercentage + '% ' + newPercentage * placeholderRatio + '%'; | |
placeholder.style.backgroundPosition = newLeftPercentage + '% ' + newTopPercentage + '%'; | |
} | |
} | |
}; | |
Image.prototype._addDragHandler = function () { | |
if (!Image.sp_isDndAddedForImages) { | |
Image.sp_isDndAddedForImages = true; | |
var self = this; | |
var dndTest = function (element) { | |
if (self._isElementInAnyImage(element)) { | |
var imageNode = self._getImageNodeFromElement(element); | |
var hasUri = !!imageNode.getAttribute('data-uri'); | |
var hasText = !!imageNode.getAttribute('data-tooltip'); | |
return hasUri && hasText; | |
} else { | |
return false; | |
} | |
}; | |
var dndGetData = function (element) { | |
var imageNode = self._getImageNodeFromElement(element); | |
var uri = imageNode.getAttribute('data-uri'); | |
var text = imageNode.getAttribute('data-tooltip'); | |
var urls = [uri.toSpotifyURL()]; | |
var links = ['<a href="' + urls[0] + '">' + text + '</a>']; | |
return { | |
'text/plain': urls, | |
'text/html': links | |
}; | |
}; | |
var dndGetText = function (element) { | |
var imageNode = self._getImageNodeFromElement(element); | |
return imageNode.getAttribute('data-tooltip'); | |
}; | |
dnd.drag.addHandler(dndTest, dndGetData, dndGetText); | |
} | |
}; | |
Image.prototype._addContextUIHandler = function () { | |
var self = this; | |
this.node.oncontextmenu = function (e) { | |
var isTargetLink = e.target.tagName.toLowerCase() === 'a'; | |
var isNodeLink = this.tagName.toLowerCase() === 'a'; | |
if (isTargetLink || isNodeLink) { | |
var link = isTargetLink ? e.target : this; | |
var uri = link.getAttribute('data-uri'); | |
uri = uri || link.href; | |
var testSpotifyURI = SpotifyApi.Exps.spotify; | |
var testSpotifyURL = SpotifyApi.Exps.http; | |
if (!uri.match(testSpotifyURI) && !uri.match(testSpotifyURL)) { | |
return; | |
} | |
var item = models.fromURI(uri); | |
if (item) { | |
var x = e.pageX - window.pageXOffset; | |
var y = e.pageY - window.pageYOffset; | |
models.client.showContextUI(item, { | |
x: x, | |
y: y | |
}); | |
return false; | |
} | |
return; | |
} | |
if (self._item) { | |
var x = e.pageX - window.pageXOffset; | |
var y = e.pageY - window.pageYOffset; | |
models.client.showContextUI(self._item, { | |
x: x, | |
y: y | |
}); | |
return false; | |
} | |
}; | |
}; | |
Image.prototype._isElementInAnyImage = function (element) { | |
return this._getImageNodeFromElement(element) !== document.documentElement ? true : false; | |
}; | |
Image.prototype._getImageNodeFromElement = function (element) { | |
while (!css.hasClass(element, 'sp-image') && element !== document) { | |
element = element.parentNode; | |
} | |
return element !== document ? element : document.documentElement; | |
}; | |
Image.prototype._getArtistsAsString = function (artists) { | |
var output = ''; | |
for (var i = 0, l = artists.length; i < l; i++) { | |
output += artists[i].name + (i < l - 1 ? ', ' : ''); | |
} | |
return output; | |
}; | |
var SIZES = { | |
placeholder: { | |
images: { | |
'1': { | |
x: 0, | |
y: 0, | |
width: 300, | |
height: 300 | |
}, | |
'2': { | |
x: 300, | |
y: 0, | |
width: 150, | |
height: 150 | |
}, | |
'3': { | |
x: 450, | |
y: 0, | |
width: 128, | |
height: 128 | |
}, | |
'4': { | |
x: 300, | |
y: 150, | |
width: 64, | |
height: 64 | |
}, | |
'5': { | |
x: 300, | |
y: 214, | |
width: 40, | |
height: 40 | |
}, | |
'empty': {} | |
}, | |
total: { | |
width: 578, | |
height: 1500, | |
numTypes: 5 | |
}, | |
offsets: { | |
'Album': 0, | |
'Artist': 1, | |
'Playlist': 2, | |
'Track': 3, | |
'User': 4 | |
} | |
} | |
}; | |
var tempImage = document.createElement('div'); | |
tempImage.className = 'sp-image-preloader'; | |
document.body.appendChild(tempImage); | |
setTimeout(function () { | |
document.body.removeChild(tempImage); | |
}, 5000); | |
}(require('node_modules/views/@loc.loc/strings/image.lang'), require('node_modules/views/scripts/utils/device.js'), require('node_modules/api/scripts/models.js'), require('node_modules/api/scripts/toplists.js').Toplist, require('node_modules/views/scripts/utils/css.js'), require('node_modules/views/scripts/utils/dom.js'), require('node_modules/views/scripts/utils/dnd.js'), require('node_modules/views/scripts/utils/touch.js'), require('node_modules/views/scripts/buttons.js'))); | |
}, | |
'node_modules/mout/collection/make_.js': function (require, module, exports, global, __filename, __dirname) { | |
var slice = require('node_modules/mout/array/slice.js'); | |
function makeCollectionMethod(arrMethod, objMethod, defaultReturn) { | |
return function () { | |
var args = slice(arguments); | |
if (args[0] == null) { | |
return defaultReturn; | |
} | |
return typeof args[0].length === 'number' ? arrMethod.apply(null, args) : objMethod.apply(null, args); | |
}; | |
} | |
module.exports = makeCollectionMethod; | |
}, | |
'node_modules/mout/collection/forEach.js': function (require, module, exports, global, __filename, __dirname) { | |
var make = require('node_modules/mout/collection/make_.js'); | |
var arrForEach = require('node_modules/mout/array/forEach.js'); | |
var objForEach = require('node_modules/mout/object/forOwn.js'); | |
module.exports = make(arrForEach, objForEach); | |
}, | |
'node_modules/mout/object/values.js': function (require, module, exports, global, __filename, __dirname) { | |
var forOwn = require('node_modules/mout/object/forOwn.js'); | |
function values(obj) { | |
var vals = []; | |
forOwn(obj, function (val, key) { | |
vals.push(val); | |
}); | |
return vals; | |
} | |
module.exports = values; | |
}, | |
'node_modules/mout/string/WHITE_SPACES.js': function (require, module, exports, global, __filename, __dirname) { | |
module.exports = [ | |
' ', | |
'\n', | |
'\r', | |
'\t', | |
'\f', | |
'\x0B', | |
'\xA0', | |
'\u1680', | |
'\u180E', | |
'\u2000', | |
'\u2001', | |
'\u2002', | |
'\u2003', | |
'\u2004', | |
'\u2005', | |
'\u2006', | |
'\u2007', | |
'\u2008', | |
'\u2009', | |
'\u200A', | |
'\u2028', | |
'\u2029', | |
'\u202F', | |
'\u205F', | |
'\u3000' | |
]; | |
}, | |
'node_modules/mout/lang/toString.js': function (require, module, exports, global, __filename, __dirname) { | |
function toString(val) { | |
return val == null ? '' : val.toString(); | |
} | |
module.exports = toString; | |
}, | |
'node_modules/mout/string/ltrim.js': function (require, module, exports, global, __filename, __dirname) { | |
var toString = require('node_modules/mout/lang/toString.js'); | |
var WHITE_SPACES = require('node_modules/mout/string/WHITE_SPACES.js'); | |
function ltrim(str, chars) { | |
str = toString(str); | |
chars = chars || WHITE_SPACES; | |
var start = 0, len = str.length, charLen = chars.length, found = true, i, c; | |
while (found && start < len) { | |
found = false; | |
i = -1; | |
c = str.charAt(start); | |
while (++i < charLen) { | |
if (c === chars[i]) { | |
found = true; | |
start++; | |
break; | |
} | |
} | |
} | |
return start >= len ? '' : str.substr(start, len); | |
} | |
module.exports = ltrim; | |
}, | |
'node_modules/mout/string/rtrim.js': function (require, module, exports, global, __filename, __dirname) { | |
var toString = require('node_modules/mout/lang/toString.js'); | |
var WHITE_SPACES = require('node_modules/mout/string/WHITE_SPACES.js'); | |
function rtrim(str, chars) { | |
str = toString(str); | |
chars = chars || WHITE_SPACES; | |
var end = str.length - 1, charLen = chars.length, found = true, i, c; | |
while (found && end >= 0) { | |
found = false; | |
i = -1; | |
c = str.charAt(end); | |
while (++i < charLen) { | |
if (c === chars[i]) { | |
found = true; | |
end--; | |
break; | |
} | |
} | |
} | |
return end >= 0 ? str.substring(0, end + 1) : ''; | |
} | |
module.exports = rtrim; | |
}, | |
'node_modules/mout/string/trim.js': function (require, module, exports, global, __filename, __dirname) { | |
var toString = require('node_modules/mout/lang/toString.js'); | |
var WHITE_SPACES = require('node_modules/mout/string/WHITE_SPACES.js'); | |
var ltrim = require('node_modules/mout/string/ltrim.js'); | |
var rtrim = require('node_modules/mout/string/rtrim.js'); | |
function trim(str, chars) { | |
str = toString(str); | |
chars = chars || WHITE_SPACES; | |
return ltrim(rtrim(str, chars), chars); | |
} | |
module.exports = trim; | |
}, | |
'node_modules/mout/lang/isKind.js': function (require, module, exports, global, __filename, __dirname) { | |
var kindOf = require('node_modules/mout/lang/kindOf.js'); | |
function isKind(val, kind) { | |
return kindOf(val) === kind; | |
} | |
module.exports = isKind; | |
}, | |
'node_modules/mout/lang/isObject.js': function (require, module, exports, global, __filename, __dirname) { | |
var isKind = require('node_modules/mout/lang/isKind.js'); | |
function isObject(val) { | |
return isKind(val, 'Object'); | |
} | |
module.exports = isObject; | |
}, | |
'node_modules/mout/lang/isArray.js': function (require, module, exports, global, __filename, __dirname) { | |
var isKind = require('node_modules/mout/lang/isKind.js'); | |
var isArray = Array.isArray || function (val) { | |
return isKind(val, 'Array'); | |
}; | |
module.exports = isArray; | |
}, | |
'node_modules/mout/object/deepMatches.js': function (require, module, exports, global, __filename, __dirname) { | |
var forOwn = require('node_modules/mout/object/forOwn.js'); | |
var isArray = require('node_modules/mout/lang/isArray.js'); | |
function containsMatch(array, pattern) { | |
var i = -1, length = array.length; | |
while (++i < length) { | |
if (deepMatches(array[i], pattern)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
function matchArray(target, pattern) { | |
var i = -1, patternLength = pattern.length; | |
while (++i < patternLength) { | |
if (!containsMatch(target, pattern[i])) { | |
return false; | |
} | |
} | |
return true; | |
} | |
function matchObject(target, pattern) { | |
var result = true; | |
forOwn(pattern, function (val, key) { | |
if (!deepMatches(target[key], val)) { | |
return result = false; | |
} | |
}); | |
return result; | |
} | |
function deepMatches(target, pattern) { | |
if (target && typeof target === 'object') { | |
if (isArray(target) && isArray(pattern)) { | |
return matchArray(target, pattern); | |
} else { | |
return matchObject(target, pattern); | |
} | |
} else { | |
return target === pattern; | |
} | |
} | |
module.exports = deepMatches; | |
}, | |
'node_modules/mout/function/makeIterator_.js': function (require, module, exports, global, __filename, __dirname) { | |
var identity = require('node_modules/mout/function/identity.js'); | |
var prop = require('node_modules/mout/function/prop.js'); | |
var deepMatches = require('node_modules/mout/object/deepMatches.js'); | |
function makeIterator(src, thisObj) { | |
if (src == null) { | |
return identity; | |
} | |
switch (typeof src) { | |
case 'function': | |
return typeof thisObj !== 'undefined' ? function (val, i, arr) { | |
return src.call(thisObj, val, i, arr); | |
} : src; | |
case 'object': | |
return function (val) { | |
return deepMatches(val, src); | |
}; | |
case 'string': | |
case 'number': | |
return prop(src); | |
} | |
} | |
module.exports = makeIterator; | |
}, | |
'node_modules/mout/object/filter.js': function (require, module, exports, global, __filename, __dirname) { | |
var forOwn = require('node_modules/mout/object/forOwn.js'); | |
var makeIterator = require('node_modules/mout/function/makeIterator_.js'); | |
function filterValues(obj, callback, thisObj) { | |
callback = makeIterator(callback, thisObj); | |
var output = {}; | |
forOwn(obj, function (value, key, obj) { | |
if (callback(value, key, obj)) { | |
output[key] = value; | |
} | |
}); | |
return output; | |
} | |
module.exports = filterValues; | |
}, | |
'node_modules/mout/array/filter.js': function (require, module, exports, global, __filename, __dirname) { | |
var makeIterator = require('node_modules/mout/function/makeIterator_.js'); | |
function filter(arr, callback, thisObj) { | |
callback = makeIterator(callback, thisObj); | |
var results = []; | |
if (arr == null) { | |
return results; | |
} | |
var i = -1, len = arr.length, value; | |
while (++i < len) { | |
value = arr[i]; | |
if (callback(value, i, arr)) { | |
results.push(value); | |
} | |
} | |
return results; | |
} | |
module.exports = filter; | |
}, | |
'node_modules/mout/array/unique.js': function (require, module, exports, global, __filename, __dirname) { | |
var filter = require('node_modules/mout/array/filter.js'); | |
function unique(arr, compare) { | |
compare = compare || isEqual; | |
return filter(arr, function (item, i, arr) { | |
var n = arr.length; | |
while (++i < n) { | |
if (compare(item, arr[i])) { | |
return false; | |
} | |
} | |
return true; | |
}); | |
} | |
function isEqual(a, b) { | |
return a === b; | |
} | |
module.exports = unique; | |
}, | |
'node_modules/mout/array/map.js': function (require, module, exports, global, __filename, __dirname) { | |
var makeIterator = require('node_modules/mout/function/makeIterator_.js'); | |
function map(arr, callback, thisObj) { | |
callback = makeIterator(callback, thisObj); | |
var results = []; | |
if (arr == null) { | |
return results; | |
} | |
var i = -1, len = arr.length; | |
while (++i < len) { | |
results[i] = callback(arr[i], i, arr); | |
} | |
return results; | |
} | |
module.exports = map; | |
}, | |
'node_modules/spotify-live/util/range.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var prime = require('node_modules/prime/index.js'); | |
var map = require('node_modules/mout/array/map.js'), slice = require('node_modules/mout/array/slice.js'); | |
var Range = prime({ | |
constructor: function Range(start, end) { | |
this.update(start, end); | |
}, | |
update: function (start, end) { | |
if (start != null) | |
this.start = start; | |
if (end != null) | |
this.end = end; | |
if (this.start == null || this.end == null) | |
throw new Error('invalid range'); | |
if (this.start > this.end) | |
throw new Error('invalid range'); | |
this.length = this.end - this.start; | |
return this; | |
}, | |
copy: function () { | |
return new Range(this.start, this.end); | |
}, | |
above: function (range) { | |
if (!range) | |
return false; | |
return this.start >= range.end; | |
}, | |
below: function (range) { | |
if (!range) | |
return false; | |
return this.end <= range.start; | |
}, | |
adjacent: function (range) { | |
if (!range) | |
return false; | |
return this.end === range.start || this.start === range.end; | |
}, | |
intersects: function (range) { | |
if (!range) | |
return false; | |
return !this.above(range) && !this.below(range); | |
}, | |
contains: function (range) { | |
if (!range) | |
return false; | |
return this.start <= range.start && this.end >= range.end; | |
}, | |
contained: function (range) { | |
var ranges; | |
if (range instanceof Range) | |
ranges = arguments; | |
else | |
ranges = range; | |
if (!ranges) | |
return false; | |
for (var i = 0; range = ranges[i]; i++) { | |
if (range.start <= this.start && range.end >= this.end) | |
return true; | |
} | |
return false; | |
}, | |
fits: function (prev, next) { | |
if (!prev && !next) | |
return true; | |
if (!prev) | |
return this.end <= next.start; | |
if (!next) | |
return this.start >= prev.end; | |
return this.start >= prev.end && this.end <= next.start; | |
}, | |
between: function (prev, next) { | |
if (!prev && !next) | |
return this.copy(); | |
if (!prev) | |
return this.start >= next.start ? null : new Range(this.start, Math.min(this.end, next.start)); | |
if (!next) | |
return this.end <= prev.end ? null : new Range(Math.max(prev.end, this.start), this.end); | |
return this.end > prev.end && this.start < next.start ? new Range(Math.max(prev.end, this.start), Math.min(next.start, this.end)) : null; | |
}, | |
intersection: function (range) { | |
var intersected = [], ranges; | |
if (range instanceof Range) | |
ranges = arguments; | |
else | |
ranges = range; | |
for (var k = 0; k < ranges.length; k++) { | |
var r = ranges[k]; | |
if (this.below(r)) | |
break; | |
if (r.intersects(this)) | |
intersected.push(new Range(Math.max(this.start, r.start), Math.min(this.end, r.end))); | |
} | |
return intersected; | |
}, | |
subtract: function (range) { | |
var subtracted = [], ranges; | |
if (range instanceof Range) | |
ranges = arguments; | |
else | |
ranges = range; | |
for (var k = -1; k < ranges.length; k++) { | |
var prev = ranges[k]; | |
var next = ranges[k + 1]; | |
var between = this.between(prev, next); | |
if (between) | |
subtracted.push(between); | |
} | |
return subtracted; | |
}, | |
extract: function (range) { | |
var ranges = range instanceof Range ? slice(arguments) : range.slice(); | |
for (var k = 0; k < ranges.length; k++) { | |
var prev = ranges[k - 1]; | |
var next = ranges[k]; | |
var newRange = null; | |
if (this.below(next)) { | |
newRange = new Range(next.start - this.length, next.end - this.length); | |
} else if (this.intersects(next)) { | |
var subtracted = next.subtract(this); | |
if (subtracted.length === 2) { | |
newRange = new Range(subtracted[0].start, subtracted[1].end - this.length); | |
} else if (subtracted.length === 1) { | |
if (next.end > this.end) { | |
newRange = new Range(subtracted[0].start - this.length, subtracted[0].end - this.length); | |
} else if (this.start > next.start) { | |
newRange = new Range(subtracted[0].start, subtracted[0].end); | |
} | |
} else { | |
ranges.splice(k--, 1); | |
} | |
} else { | |
ranges.splice(k, 1, next.copy()); | |
} | |
if (newRange) { | |
if (prev && prev.end === newRange.start) { | |
ranges.splice(k-- - 1, 2, new Range(prev.start, newRange.end)); | |
} else { | |
ranges.splice(k, 1, newRange); | |
} | |
} | |
} | |
return ranges; | |
}, | |
insert: function (range) { | |
var ranges = range instanceof Range ? slice(arguments) : range.slice(); | |
for (var k = 0; k < ranges.length; k++) { | |
var next = ranges[k]; | |
if (this.start >= next.end) { | |
ranges.splice(k, 1, next.copy()); | |
} else if (this.start > next.start && this.start < next.end) { | |
ranges.splice(k, 1, new Range(next.start, this.start), new Range(this.start, next.end)); | |
} else { | |
ranges.splice(k, 1, new Range(next.start + this.length, next.end + this.length)); | |
} | |
} | |
return this.merge(ranges); | |
}, | |
merge: function (range) { | |
var ranges; | |
if (range instanceof Range) | |
ranges = arguments; | |
else | |
ranges = range; | |
ranges = map(ranges, function (r) { | |
return r.copy(); | |
}); | |
if (!ranges.length) | |
return [this.copy()]; | |
for (var k = -1, l = ranges.length; k < l; k++) { | |
var prev = ranges[k]; | |
var next = ranges[k + 1]; | |
var between = this.between(prev, next); | |
if (between) { | |
if (!prev && next) { | |
if (between.end === next.start) { | |
next.update(between.start, next.end); | |
} else { | |
k++; | |
ranges.unshift(between); | |
} | |
} else if (prev && next) { | |
if (prev.end === between.start && between.end === next.start) { | |
prev.update(prev.start, next.end); | |
ranges.splice(k-- + 1, 1); | |
} else if (prev.end === between.start) { | |
prev.update(prev.start, between.end); | |
} else if (between.end === next.start) { | |
next.update(between.start, next.end); | |
} else { | |
ranges.splice(k + 1, 0, between); | |
} | |
} else if (prev && !next) { | |
if (prev.end === between.start) { | |
prev.update(prev.start, between.end); | |
} else { | |
k++; | |
ranges.push(between); | |
} | |
} | |
} | |
} | |
return ranges; | |
}, | |
remove: function (range) { | |
var ranges; | |
if (range instanceof Range) | |
ranges = arguments; | |
else | |
ranges = range; | |
var result = []; | |
for (var i = 0; i < ranges.length; i++) { | |
var remaining = ranges[i].subtract(this); | |
if (remaining.length) { | |
result.push.apply(result, remaining); | |
} | |
} | |
return result; | |
}, | |
toIndices: function () { | |
var indices = []; | |
for (var i = this.start; i < this.end; i++) | |
indices.push(i); | |
return indices; | |
}, | |
toString: function () { | |
return [ | |
this.start, | |
this.end | |
] + ''; | |
} | |
}); | |
Range.fromString = function (string) { | |
var parts = string.split(','); | |
return new Range(+parts[0], +parts[1]); | |
}; | |
Range.fromIndices = function (indices) { | |
indices.sort(function (a, b) { | |
return a > b ? 1 : -1; | |
}); | |
var ranges = [], rstart, rend; | |
for (var i = 0; i < indices.length; i++) { | |
rstart = indices[i]; | |
rend = rstart; | |
while (indices[i + 1] - indices[i] === 1) { | |
rend = indices[i + 1]; | |
i++; | |
} | |
ranges.push(new Range(rstart, rend + 1)); | |
} | |
return ranges; | |
}; | |
var exports = function (start, end) { | |
return new Range(start, end); | |
}; | |
module.exports = exports; | |
exports.Range = Range; | |
exports.fromIndices = Range.fromIndices; | |
exports.fromString = Range.fromString; | |
}, | |
'node_modules/spotify-live/util/parser.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var range = require('node_modules/spotify-live/util/range.js'); | |
var parse = function (string) { | |
var results = []; | |
var b = '('; | |
var e = ')'; | |
var bs = '['; | |
var es = ']'; | |
var s = ' '; | |
var cm = ','; | |
var bindex = 0, eindex = 0; | |
var count = 0; | |
var strings = []; | |
var objects = []; | |
var key = ''; | |
var ibs = 0; | |
var bnes = 0, snes = 0; | |
for (var i = 0; i < string.length + 1; i++) { | |
var c = string.charAt(i); | |
if (!count) { | |
if (!ibs) { | |
if (/\w/.exec(c)) { | |
key += c; | |
} else if (key && (!c || c === s || c === b || c === cm || c === bs)) { | |
if (c === bs) { | |
snes++; | |
ibs = i; | |
} else { | |
objects.push({ key: key }); | |
key = ''; | |
} | |
} | |
} else { | |
if (c === es) { | |
snes--; | |
var contents = string.substring(ibs + 1, i); | |
objects.push({ | |
key: key, | |
range: range.fromString(contents) | |
}); | |
ibs = 0; | |
key = ''; | |
} | |
} | |
} | |
if (c === b) { | |
bnes++; | |
if (!count++) | |
bindex = i + 1; | |
} else if (c === e) { | |
bnes--; | |
if (--count === 0) { | |
eindex = i; | |
strings[objects.length - 1] = string.substring(bindex, eindex); | |
} | |
} | |
if (bnes === -1 || snes === -1) | |
throw new Error('query syntax error'); | |
} | |
if (bnes !== 0 || snes !== 0) | |
throw new Error('query syntax error'); | |
for (var i = 0; i < objects.length; i++) { | |
var str = strings[i], obj = objects[i], value = { key: obj.key }; | |
if (str) | |
value.query = parse(str); | |
if (obj.range) | |
value.range = obj.range; | |
results.push(value); | |
} | |
return results; | |
}; | |
module.exports = parse; | |
}, | |
'node_modules/mout/array/some.js': function (require, module, exports, global, __filename, __dirname) { | |
var makeIterator = require('node_modules/mout/function/makeIterator_.js'); | |
function some(arr, callback, thisObj) { | |
callback = makeIterator(callback, thisObj); | |
var result = false; | |
if (arr == null) { | |
return result; | |
} | |
var i = -1, len = arr.length; | |
while (++i < len) { | |
if (callback(arr[i], i, arr)) { | |
result = true; | |
break; | |
} | |
} | |
return result; | |
} | |
module.exports = some; | |
}, | |
'node_modules/mout/array/difference.js': function (require, module, exports, global, __filename, __dirname) { | |
var unique = require('node_modules/mout/array/unique.js'); | |
var filter = require('node_modules/mout/array/filter.js'); | |
var some = require('node_modules/mout/array/some.js'); | |
var contains = require('node_modules/mout/array/contains.js'); | |
var slice = require('node_modules/mout/array/slice.js'); | |
function difference(arr) { | |
var arrs = slice(arguments, 1), result = filter(unique(arr), function (needle) { | |
return !some(arrs, function (haystack) { | |
return contains(haystack, needle); | |
}); | |
}); | |
return result; | |
} | |
module.exports = difference; | |
}, | |
'node_modules/mout/collection/map.js': function (require, module, exports, global, __filename, __dirname) { | |
var isObject = require('node_modules/mout/lang/isObject.js'); | |
var values = require('node_modules/mout/object/values.js'); | |
var arrMap = require('node_modules/mout/array/map.js'); | |
var makeIterator = require('node_modules/mout/function/makeIterator_.js'); | |
function map(list, callback, thisObj) { | |
callback = makeIterator(callback, thisObj); | |
if (isObject(list) && list.length == null) { | |
list = values(list); | |
} | |
return arrMap(list, function (val, key, list) { | |
return callback(val, key, list); | |
}); | |
} | |
module.exports = map; | |
}, | |
'node_modules/finally/index.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var prime = require('node_modules/prime/index.js'); | |
var kindOf = require('node_modules/mout/lang/kindOf.js'); | |
var map = require('node_modules/mout/array/map.js'); | |
var slice = require('node_modules/mout/array/slice.js'); | |
var forEach = require('node_modules/mout/array/forEach.js'); | |
var reduce = require('node_modules/mout/array/reduce.js'); | |
var each = require('node_modules/mout/collection/forEach.js'); | |
var cmap = require('node_modules/mout/collection/map.js'); | |
var push_ = Array.prototype.push; | |
var Flow = prime({ | |
constructor: function Flow() { | |
this._seq = []; | |
}, | |
then: function () { | |
this._push(this._callbacks(arguments)); | |
return this; | |
}, | |
_parallel: function (parallel, args) { | |
var self = this; | |
return function () { | |
var control = new Controller(self, self._index++); | |
self._controls.push(control); | |
parallel.apply(control, args ? args.concat(slice(arguments)) : arguments); | |
}; | |
}, | |
_push: function (parallels, args) { | |
if (!parallels.length) | |
return; | |
this._seq.push(map(parallels, function (parallel) { | |
return this._parallel(parallel, args); | |
}, this)); | |
}, | |
_callbacks: function (callbacks) { | |
return reduce(callbacks, function (a, b) { | |
if (kindOf(b) === 'Array') | |
push_.apply(a, b); | |
else | |
a.push(b); | |
return a; | |
}, []); | |
}, | |
sequential: function (object) { | |
var callbacks = this._callbacks(slice(arguments, 1)); | |
each(object, function (value, key) { | |
this._push(callbacks, [ | |
value, | |
key | |
]); | |
}, this); | |
return this; | |
}, | |
parallel: function (object, parallel) { | |
var parallels = cmap(object, function (value, key) { | |
return this._parallel(parallel, [ | |
value, | |
key | |
]); | |
}, this); | |
if (parallels.length) | |
this._seq.push(parallels); | |
return this; | |
}, | |
finally: function () { | |
this.then.apply(this, arguments); | |
this._continue.call(this); | |
return this; | |
}, | |
run: function () { | |
this._continue.apply(this, arguments); | |
return this; | |
}, | |
_break: function () { | |
this._seq.splice(0, this._seq.length - 1); | |
this._continue.apply(this, arguments); | |
}, | |
_spread: function (error, args) { | |
var seq = this._next(); | |
if (!seq || !(seq = seq[0])) | |
return; | |
if (!args || !args.length) | |
args = [undefined]; | |
this._length = args.length; | |
forEach(args, function (arg) { | |
seq(error, arg); | |
}); | |
}, | |
_continue: function () { | |
var seq = this._next(); | |
if (!seq) | |
return; | |
this._length = seq.length; | |
var args = arguments; | |
forEach(seq, function (parallel) { | |
parallel.apply(null, args); | |
}); | |
}, | |
_next: function () { | |
var seq = this._seq.shift(); | |
if (!seq) | |
return; | |
if (this._controls) | |
forEach(this._controls, function (control) { | |
control._kill(); | |
}); | |
this._arguments = []; | |
this._errors = []; | |
this._controls = []; | |
this._index = 0; | |
return seq; | |
}, | |
_done: function (index, error, data) { | |
this._arguments[index] = data; | |
if (error) | |
this._errors.push(error); | |
if (!--this._length) { | |
var errors = null; | |
if (this._errors.length === 1) | |
errors = this._errors[0]; | |
else if (this._errors.length) | |
errors = new Error(map(this._errors, function (e) { | |
return e.message; | |
}).join('\n')); | |
this._continue.apply(this, [errors].concat(this._arguments)); | |
} else | |
this._controls[index]._kill(); | |
} | |
}); | |
var Controller = function Controller(flow, index) { | |
var dead; | |
this._kill = function () { | |
dead = true; | |
}; | |
this.break = function () { | |
if (!dead) | |
flow._break.apply(flow, arguments); | |
}; | |
this.continue = function () { | |
if (!dead) | |
flow._continue.apply(flow, arguments); | |
}; | |
this.spread = function (error, args) { | |
if (!dead) | |
flow._spread(error, args); | |
}; | |
var done = this.done = function (error, data) { | |
if (!dead) | |
flow._done.call(flow, index, error, data); | |
}; | |
}; | |
module.exports = function () { | |
var flow = new Flow(); | |
flow.then.apply(flow, arguments); | |
return flow; | |
}; | |
}, | |
'node_modules/spotify-live/index.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var prime = require('node_modules/prime/index.js'), defer = require('node_modules/prime/defer.js'), Emitter = require('node_modules/prime/emitter.js'); | |
var kindOf = require('node_modules/mout/lang/kindOf.js'), forIn = require('node_modules/mout/object/forIn.js'), isPlainObject = require('node_modules/mout/lang/isPlainObject.js'), objectFilter = require('node_modules/mout/object/filter.js'), mixIn = require('node_modules/mout/object/mixIn.js'), difference = require('node_modules/mout/array/difference.js'), forEach = require('node_modules/mout/array/forEach.js'), combine = require('node_modules/mout/array/combine.js'), append = require('node_modules/mout/array/append.js'), remove = require('node_modules/mout/array/remove.js'), filter = require('node_modules/mout/array/filter.js'), map = require('node_modules/mout/array/map.js'); | |
var flow = require('node_modules/finally/index.js'); | |
var splice_ = Array.prototype.splice, slice_ = Array.prototype.slice; | |
var EMIT_SYNC = Emitter.EMIT_SYNC; | |
var Range = require('node_modules/spotify-live/util/range.js').Range; | |
var parse = require('node_modules/spotify-live/util/parser.js'); | |
var kind = function (object) { | |
if (object instanceof LiveList || object instanceof LiveModel) | |
return 'live'; | |
if (isPlainObject(object)) | |
return kindOf(object.operations) === 'Array' ? 'list' : 'object'; | |
var ko = kindOf(object); | |
if (ko === 'Array') | |
return 'list'; | |
if (ko === 'String') | |
return 'string'; | |
return null; | |
}; | |
var serialize = function (value) { | |
return value instanceof LiveModel || value instanceof LiveList ? value.serialize() : value; | |
}; | |
var LiveList = prime({ | |
mixin: Emitter, | |
constructor: function LiveList(length) { | |
this.length = length || 0; | |
this._index = []; | |
}, | |
_mesh: function (index, howMany, items, isPublish) { | |
index = +index || 0; | |
howMany = +howMany || 0; | |
if (!items) | |
items = []; | |
if (index > this.length) | |
this.length = index; | |
if (index + howMany > this.length) | |
howMany = this.length - index; | |
var length = items.length; | |
var remove = new Range(index, index + howMany); | |
var insert = new Range(index, index + length); | |
if (howMany) { | |
this._index = remove.extract(this._index); | |
} | |
if (length) { | |
this._index = insert.insert(this._index); | |
} | |
items = map(items, function (item) { | |
switch (kind(item)) { | |
case 'object': | |
return modelify(item, isPublish); | |
case 'list': | |
return listify(item, isPublish); | |
} | |
return item; | |
}); | |
var removed; | |
var limit = 60000; | |
if (items.length > limit) { | |
var cursor = 0; | |
var doRemove = !!howMany; | |
while (cursor < items.length || doRemove) { | |
var chunk = items.slice(cursor, cursor + limit); | |
var spliced = splice_.apply(this, [ | |
cursor + index, | |
doRemove ? howMany : 0 | |
].concat(chunk)); | |
if (doRemove) | |
removed = spliced; | |
cursor += limit; | |
doRemove = false; | |
} | |
} else { | |
removed = splice_.apply(this, [ | |
index, | |
howMany | |
].concat(items)); | |
} | |
if (howMany || length) { | |
var event = { | |
index: index, | |
insert: items, | |
remove: removed | |
}; | |
this.emit('update', event); | |
if (isPublish) | |
this.emit('publish', event); | |
} | |
return removed; | |
}, | |
update: function (item) { | |
return this._update(item); | |
}, | |
publish: function (item) { | |
return this._update(item, true); | |
}, | |
_update: function (item, isPublish) { | |
var operations; | |
switch (kindOf(item)) { | |
case 'Array': | |
operations = [{ | |
index: 0, | |
remove: this.length, | |
insert: item | |
}]; | |
break; | |
case 'Object': | |
operations = item.operations; | |
break; | |
default: | |
operations = []; | |
} | |
forEach(operations, function (op) { | |
if (op.length) | |
this.length = op.length; | |
this._mesh(op.index, op.remove, op.insert, isPublish); | |
}, this); | |
return this; | |
}, | |
serialize: function () { | |
var array; | |
if (this._index.length === 1 && this.length === this._index[this._index.length - 1].end) | |
array = []; | |
else | |
array = { length: this.length }; | |
forEach(this._index, function (range) { | |
for (var i = range.start; i < range.end; i++) | |
array[i] = serialize(this[i]); | |
}, this); | |
return array; | |
}, | |
query: function (selector, callback, mode) { | |
if (!mode) | |
mode = ASYNC; | |
var data; | |
queryList(this, { query: parse(selector) }, function (error, json, wasSync) { | |
if (callback) { | |
if (wasSync && mode === ASYNC) | |
defer(function () { | |
callback(null, json); | |
}); | |
else | |
callback(null, json); | |
} else { | |
data = json; | |
} | |
}, !callback || mode === SYNC); | |
return callback ? this : data; | |
}, | |
_wait: function (required) { | |
var self = this; | |
if (!this._waiting) | |
this._waiting = []; | |
if (!this._required) | |
this._required = []; | |
forEach(this._waiting, function (range) { | |
required = range.remove(required); | |
}); | |
if (required.length) { | |
var requiredCountForTick = this._required.length; | |
forEach(required, function (r) { | |
self._required = r.merge(self._required); | |
}); | |
if (requiredCountForTick === 0) { | |
defer(function () { | |
forEach(this._required, function (r) { | |
self._waiting = r.merge(self._waiting); | |
}); | |
this.emit('wait', this._required); | |
var timeOut; | |
if (live.debug) { | |
timeOut = setTimeout(function () { | |
console.warn(self, 'is waiting for', self._waiting); | |
timeOut = null; | |
}, 2000); | |
} | |
forEach(this._required, function (range) { | |
self.get(range, function (error) { | |
if (error) | |
return console.error(error); | |
self._waiting = range.remove(self._waiting); | |
if (live.debug) { | |
console.log(self, 'done waiting for', range); | |
if (!self._waiting.length && timeOut) { | |
clearTimeout(timeOut); | |
} | |
} | |
}); | |
}); | |
delete this._required; | |
}, this); | |
} | |
} | |
}, | |
_get: function (ranges, callback) { | |
var self = this; | |
var getRequired = function () { | |
var diff = self._index; | |
forEach(ranges, function (req) { | |
diff = req.subtract(diff); | |
}); | |
return diff; | |
}; | |
var required = getRequired(); | |
if (required.length) { | |
this._wait(required); | |
var check = function () { | |
var r = getRequired(); | |
if (!r.length) { | |
self.off('update', check); | |
callback(); | |
} | |
}; | |
this.on('update', check); | |
} else { | |
callback(null, true); | |
} | |
}, | |
get: function (what, toIndex) { | |
if (arguments.length === 1 && typeof what === 'number') | |
return this[what]; | |
var self = this; | |
var args = slice_.call(arguments); | |
var callback, mode, requested; | |
var isResultSet = false; | |
var isSingleValue = false; | |
forEach(args, function (arg, i) { | |
if (kindOf(arg) === 'Function') { | |
var split = args.splice(i, 2); | |
callback = split[0]; | |
mode = split[1] || ASYNC; | |
return false; | |
} | |
}); | |
if (what instanceof Range) { | |
requested = args; | |
if (requested.length > 1) | |
isResultSet = true; | |
} else if (kindOf(what) === 'Array') { | |
isResultSet = true; | |
requested = what; | |
} else { | |
var hasToIndex = !isNaN(+toIndex); | |
if (!hasToIndex) | |
toIndex = what + 1; | |
isSingleValue = !hasToIndex; | |
requested = [new Range(what, toIndex)]; | |
} | |
var result = function (asResultSet) { | |
if (isSingleValue) | |
return self[what]; | |
if (asResultSet) { | |
return map(requested, function (range) { | |
return slice_.call(self, range.start, range.end); | |
}); | |
} else { | |
var range = requested[0]; | |
return slice_.call(self, range.start, range.end); | |
} | |
}; | |
var done = function () { | |
return callback.apply(null, [null].concat(result(true))); | |
}; | |
if (!callback) { | |
return result(isResultSet); | |
} else if (mode === SYNC) { | |
done(); | |
} else { | |
this._get(requested, function (error, sync) { | |
if (sync && mode === ASYNC) | |
defer(done); | |
else | |
done(); | |
}); | |
} | |
return this; | |
} | |
}); | |
var cache = {}; | |
var LiveModel = prime({ | |
mixin: Emitter, | |
constructor: function LiveModel(uri) { | |
this.uri = uri; | |
this._data = {}; | |
this.emit('init', EMIT_SYNC); | |
}, | |
delete: function (key) { | |
var data = this._data; | |
var value = data[key]; | |
delete data[key]; | |
return value; | |
}, | |
emit: function () { | |
var uri = this.uri; | |
if (uri) { | |
var keys = emitters.keys; | |
var values = emitters.values; | |
var args = slice_.call(arguments); | |
args.splice(1, 0, this); | |
forEach(keys, function (key, i) { | |
if (uri.match(key)) { | |
var emitter = values[i]; | |
emitter.emit.apply(emitter, args); | |
} | |
}); | |
} | |
Emitter.prototype.emit.apply(this, arguments); | |
}, | |
update: function (object) { | |
return this._update(object); | |
}, | |
publish: function (object) { | |
return this._update(object, true); | |
}, | |
_update: function (object, isPublish) { | |
var data = this._data; | |
var _emit = this._emit; | |
if (!_emit) { | |
var old = mixIn({}, data); | |
_emit = this._emit = { | |
publish: {}, | |
update: {} | |
}; | |
defer(function () { | |
var update = objectFilter(_emit.update, function (v, key) { | |
return data[key] !== old[key]; | |
}); | |
var publish = objectFilter(_emit.publish, function (v, key) { | |
return data[key] !== old[key]; | |
}); | |
delete this._emit; | |
var k; | |
for (k in update) { | |
this.emit('update', update, EMIT_SYNC); | |
break; | |
} | |
for (k in publish) { | |
this.emit('publish', publish, EMIT_SYNC); | |
break; | |
} | |
}, this); | |
} | |
forIn(object, function (value, key) { | |
var prev = data[key]; | |
var k = kind(value); | |
if (k === 'list' && prev instanceof LiveList) { | |
prev._update(value, isPublish); | |
} else { | |
if (k === 'object') { | |
value = modelify(value, isPublish); | |
} else if (k === 'list') { | |
value = listify(value, isPublish); | |
} | |
data[key] = _emit.update[key] = value; | |
if (isPublish) | |
_emit.publish[key] = value; | |
} | |
}, this); | |
return this; | |
}, | |
serialize: function () { | |
var object = {}, self = this._data; | |
for (var key in self) | |
object[key] = serialize(self[key]); | |
return object; | |
}, | |
query: function (selector, callback, mode) { | |
if (!mode) | |
mode = ASYNC; | |
var data; | |
queryModel(this, parse(selector), function (error, json, wasSync) { | |
if (callback) { | |
if (wasSync && mode !== ASAP && mode !== SYNC) | |
defer(function () { | |
callback(null, json); | |
}); | |
else | |
callback(null, json); | |
} else { | |
data = json; | |
} | |
}, !callback || mode === SYNC); | |
return callback ? this : data; | |
}, | |
_wait: function (required) { | |
var _waiting = this._waiting || (this._waiting = []); | |
var _required = this._required || (this._required = []); | |
var newProperties = difference(required, _waiting); | |
if (newProperties.length) { | |
var requiredCountForTick = _required.length; | |
combine(_required, newProperties); | |
if (requiredCountForTick === 0) { | |
defer(function () { | |
append(_waiting, _required); | |
this.emit('wait', _required); | |
var uri, timeOut; | |
if (live.debug) { | |
uri = this.uri; | |
timeOut = setTimeout(function () { | |
console.warn(uri, 'is waiting _for', _waiting); | |
timeOut = null; | |
}, 2000); | |
} | |
var self = this; | |
forEach(_required, function (property) { | |
self.get(property, function (error) { | |
if (error) | |
return console.error(error); | |
remove(_waiting, property); | |
if (live.debug) { | |
console.log(uri, 'done waiting for', property); | |
if (!_waiting.length && timeOut) { | |
clearTimeout(timeOut); | |
} | |
} | |
}); | |
}); | |
delete this._required; | |
}, this); | |
} | |
} | |
}, | |
_get: function (keys, callback) { | |
var self = this; | |
var data = this._data; | |
var required = filter(keys, function (key) { | |
return !(key in data); | |
}); | |
if (required.length) { | |
this._wait(required); | |
var check = function (event) { | |
for (var key in event) | |
remove(required, key); | |
if (!required.length) { | |
self.off('update', check); | |
callback(); | |
} | |
}; | |
this.on('update', check); | |
} else { | |
callback(null, true); | |
} | |
}, | |
get: function (key) { | |
var data = this._data; | |
if (arguments.length === 1 && typeof key === 'string') | |
return data[key]; | |
var args = slice_.call(arguments); | |
var callback, mode, keys; | |
forEach(args, function (arg, i) { | |
if (kindOf(arg) === 'Function') { | |
var split = args.splice(i, 2); | |
callback = split[0]; | |
mode = split[1] || ASYNC; | |
return false; | |
} | |
}); | |
var isResultSet = false; | |
if (kindOf(key) === 'Array') { | |
keys = key; | |
isResultSet = true; | |
} else { | |
keys = args; | |
if (keys.length > 1) | |
isResultSet = true; | |
} | |
var result = function (asResultSet) { | |
if (asResultSet) { | |
return map(keys, function (key) { | |
return data[key]; | |
}); | |
} else { | |
return data[keys[0]]; | |
} | |
}; | |
var done = function () { | |
callback.apply(null, [null].concat(result(true))); | |
}; | |
if (!callback) { | |
return result(isResultSet); | |
} else if (mode === SYNC) { | |
done(); | |
} else { | |
this._get(keys, function (error, sync) { | |
if (sync && mode === ASYNC) | |
defer(done); | |
else | |
done(); | |
}); | |
} | |
return this; | |
} | |
}); | |
var queryModel = function (object, parsed, callback, forceSync) { | |
var data = {}; | |
if (!parsed) | |
parsed = []; | |
var keys = map(parsed, 'key'); | |
if (!keys.length) | |
return callback(null, data, true); | |
var queryValues = function (error, sync) { | |
var query = flow(); | |
query.parallel(parsed, function (req) { | |
var control = this; | |
var key = req.key; | |
var item = object._data[key]; | |
if (item instanceof LiveList) { | |
queryList(item, req, function (error, json, _sync) { | |
if (!_sync) | |
sync = false; | |
control.done(null, data[key] = json); | |
}, forceSync); | |
} else if (item instanceof LiveModel) { | |
queryModel(item, req.query, function (error, json, _sync) { | |
if (!_sync) | |
sync = false; | |
control.done(null, data[key] = json); | |
}, forceSync); | |
} else { | |
control.done(null, data[key] = item); | |
} | |
}); | |
query.finally(function (error) { | |
callback(error, data, sync); | |
}); | |
}; | |
if (forceSync) { | |
queryValues(null, true); | |
} else { | |
object._get(keys, queryValues); | |
} | |
}; | |
var queryList = function (list, parsed, callback, forceSync) { | |
var data = []; | |
var range = parsed.range; | |
if (!range) | |
range = new Range(0, list.length); | |
if (!range.length) | |
return callback(null, data, true); | |
var start = range.start; | |
var end = range.end; | |
var queryValues = function (error, sync) { | |
var parallels = []; | |
var j = 0; | |
var loop = function (item) { | |
parallels.push(function () { | |
var control = this; | |
var k = j++; | |
if (item instanceof LiveModel) { | |
queryModel(item, parsed.query, function (error, json, _sync) { | |
if (!_sync) | |
sync = false; | |
control.done(null, data[k] = json); | |
}, forceSync); | |
} else if (item instanceof LiveList) { | |
queryList(item, parsed, function (error, json, _sync) { | |
if (!_sync) | |
sync = false; | |
control.done(null, data[k] = json); | |
}, forceSync); | |
} else { | |
control.done(null, data[k] = item); | |
} | |
}); | |
}; | |
for (var i = start; i < end; i++) | |
loop(list[i], i); | |
flow(parallels).finally(function (error) { | |
callback(error, data, sync); | |
}); | |
}; | |
if (forceSync) { | |
queryValues(null, true); | |
} else { | |
list._get([range], queryValues); | |
} | |
}; | |
var subs = { | |
values: [], | |
keys: [] | |
}; | |
var inits = { | |
values: [], | |
keys: [] | |
}; | |
var modelify = function (object, isPublish) { | |
var uri = object.uri; | |
var model; | |
if (!uri) { | |
model = new LiveModel(); | |
} else { | |
var cached = cache[uri]; | |
if (cached) { | |
model = cached; | |
} else { | |
var Found; | |
forEach(subs.keys, function (match, i) { | |
if (uri.match(match)) | |
return !(Found = subs.values[i]); | |
}); | |
model = cache[uri] = Found ? new Found(uri) : new LiveModel(uri); | |
forEach(inits.keys, function (match, i) { | |
if (uri.match(match)) | |
inits.values[i].call(model, model); | |
}); | |
} | |
} | |
return model._update(object, isPublish); | |
}; | |
var listify = function (item, isPublish) { | |
var list = new LiveList(); | |
return list._update(item, isPublish); | |
}; | |
var live = function (item) { | |
if (!item) | |
item = {}; | |
switch (kind(item)) { | |
case 'list': | |
return listify(item); | |
case 'string': | |
return modelify({ uri: item }); | |
case 'object': | |
return modelify(item); | |
case 'live': | |
return item; | |
} | |
return null; | |
}; | |
live.register = function (matches, model) { | |
if (arguments.length === 1) { | |
model = matches; | |
matches = model.matches; | |
if (model.register) { | |
model.register(); | |
} else if (kindOf(model) === 'Object') { | |
forIn(model, function (model) { | |
live.register(model); | |
}); | |
return this; | |
} | |
} | |
if (model.prototype instanceof LiveModel) { | |
subs.keys.unshift(matches); | |
subs.values.unshift(model); | |
} else if (kindOf(model) === 'Function') { | |
inits.keys.push(matches); | |
inits.values.push(model); | |
} | |
return this; | |
}; | |
var emitters = { | |
keys: [], | |
values: [] | |
}; | |
live.subscribe = function (match, name, handle) { | |
if (match.matches) | |
match = match.matches; | |
var string = match.toString(); | |
var keys = emitters.keys; | |
var values = emitters.values; | |
var emitter; | |
forEach(keys, function (key, i) { | |
if (key.toString() === string) | |
return !(emitter = values[i]); | |
}); | |
if (!emitter) { | |
keys.push(match); | |
values.push(emitter = new Emitter()); | |
} | |
emitter.on(name, handle); | |
return this; | |
}; | |
live.unsubscribe = function (match, name, handle) { | |
if (match.matches) | |
match = match.matches; | |
var string = match.toString(); | |
var keys = emitters.keys; | |
var values = emitters.values; | |
var emitter; | |
forEach(keys, function (key, i) { | |
if (key.toString() === string) | |
return !(emitter = values[i]); | |
}); | |
if (emitter) | |
emitter.off(name, handle); | |
return this; | |
}; | |
var ASYNC = live.ASYNC = 0; | |
var SYNC = live.SYNC = 1; | |
var ASAP = live.ASAP = 2; | |
live.Model = LiveModel; | |
live.List = LiveList; | |
module.exports = live; | |
}, | |
'node_modules/spotify-live-models/client.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var live = require('node_modules/spotify-live/index.js'); | |
var bridge = require('node_modules/spotify-live-models/util/bridge.js').request; | |
function updateCurrentUser(model) { | |
bridge('user_metadata', ['spotify:user:@'], function (error, payload) { | |
if (error) | |
throw error; | |
model.update({ | |
currentUser: { | |
uri: 'spotify:user:' + encodeURIComponent(payload.username), | |
name: payload.name, | |
username: payload.username | |
} | |
}); | |
}); | |
} | |
function updateFeatures(model) { | |
bridge('client_features', [], function (error, payload) { | |
if (error) | |
throw error; | |
model.update({ features: payload.features }); | |
}); | |
} | |
function updateSessionData(model) { | |
bridge('session_query', [], function (error, payload) { | |
if (error) | |
throw error; | |
model.update({ session: payload }); | |
}); | |
} | |
function showContextMenu(model, event) { | |
var uris = event.uris; | |
var x = event.x; | |
var y = event.y; | |
var context = event.context && event.context.uri || null; | |
var index = event.index; | |
bridge('client_show_context_ui', [ | |
uris, | |
x, | |
y, | |
context, | |
index | |
]); | |
} | |
function onWait(model, properties) { | |
if (properties.indexOf('currentUser') > -1) { | |
updateCurrentUser(model); | |
} | |
if (properties.indexOf('features') > -1) { | |
updateFeatures(model); | |
} | |
if (properties.indexOf('session') > -1) { | |
updateSessionData(model); | |
} | |
} | |
function onInit(model) { | |
if (global.__spotify && global.__spotify.username) { | |
model.update({ currentUser: { username: global.__spotify.username } }); | |
} | |
updateCurrentUser(model); | |
} | |
var regExp = exports.matches = /^spotify:client$/; | |
exports.register = function () { | |
live.subscribe(regExp, 'wait', onWait); | |
live.subscribe(regExp, 'init', onInit); | |
live.subscribe(regExp, 'show-context-menu', showContextMenu); | |
}; | |
exports.unregister = function () { | |
live.unsubscribe(regExp, 'wait', onWait); | |
live.unsubscribe(regExp, 'init', onInit); | |
live.unsubscribe(regExp, 'show-context-menu', showContextMenu); | |
}; | |
}, | |
'node_modules/mout/array/every.js': function (require, module, exports, global, __filename, __dirname) { | |
var makeIterator = require('node_modules/mout/function/makeIterator_.js'); | |
function every(arr, callback, thisObj) { | |
callback = makeIterator(callback, thisObj); | |
var result = true; | |
if (arr == null) { | |
return result; | |
} | |
var i = -1, len = arr.length; | |
while (++i < len) { | |
if (!callback(arr[i], i, arr)) { | |
result = false; | |
break; | |
} | |
} | |
return result; | |
} | |
module.exports = every; | |
}, | |
'node_modules/spotify-events/node_modules/elements/base.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var prime = require('node_modules/prime/index.js'); | |
var forEach = require('node_modules/mout/array/forEach.js'), map = require('node_modules/mout/array/map.js'), filter = require('node_modules/mout/array/filter.js'), every = require('node_modules/mout/array/every.js'), some = require('node_modules/mout/array/some.js'); | |
var index = 0, __dc = document.__counter, counter = document.__counter = (__dc ? parseInt(__dc, 36) + 1 : 0).toString(36), key = 'uid:' + counter; | |
var uniqueID = function (n) { | |
if (n === window) | |
return 'window'; | |
if (n === document) | |
return 'document'; | |
if (n === document.documentElement) | |
return 'html'; | |
return n[key] || (n[key] = (index++).toString(36)); | |
}; | |
var instances = {}; | |
var $ = prime({ | |
constructor: function $(n, context) { | |
if (n == null) | |
return this && this.constructor === $ ? new Elements() : null; | |
var self, uid; | |
if (n.constructor !== Elements) { | |
self = new Elements(); | |
if (typeof n === 'string') { | |
if (!self.search) | |
return null; | |
self[self.length++] = context || document; | |
return self.search(n); | |
} | |
if (n.nodeType || n === window) { | |
self[self.length++] = n; | |
} else if (n.length) { | |
var uniques = {}; | |
for (var i = 0, l = n.length; i < l; i++) { | |
var nodes = $(n[i], context); | |
if (nodes && nodes.length) | |
for (var j = 0, k = nodes.length; j < k; j++) { | |
var node = nodes[j]; | |
uid = uniqueID(node); | |
if (!uniques[uid]) { | |
self[self.length++] = node; | |
uniques[uid] = true; | |
} | |
} | |
} | |
} | |
} else { | |
self = n; | |
} | |
if (!self.length) | |
return null; | |
if (self.length === 1) { | |
uid = uniqueID(self[0]); | |
return instances[uid] || (instances[uid] = self); | |
} | |
return self; | |
} | |
}); | |
var Elements = prime({ | |
inherits: $, | |
constructor: function Elements() { | |
this.length = 0; | |
}, | |
unlink: function () { | |
return this.map(function (node) { | |
delete instances[uniqueID(node)]; | |
return node; | |
}); | |
}, | |
forEach: function (method, context) { | |
forEach(this, method, context); | |
return this; | |
}, | |
map: function (method, context) { | |
return map(this, method, context); | |
}, | |
filter: function (method, context) { | |
return filter(this, method, context); | |
}, | |
every: function (method, context) { | |
return every(this, method, context); | |
}, | |
some: function (method, context) { | |
return some(this, method, context); | |
} | |
}); | |
module.exports = $; | |
}, | |
'node_modules/spotify-events/node_modules/elements/attributes.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var $ = require('node_modules/spotify-events/node_modules/elements/base.js'); | |
var trim = require('node_modules/mout/string/trim.js'), forEach = require('node_modules/mout/array/forEach.js'), filter = require('node_modules/mout/array/filter.js'), indexOf = require('node_modules/mout/array/indexOf.js'); | |
$.implement({ | |
setAttribute: function (name, value) { | |
return this.forEach(function (node) { | |
node.setAttribute(name, value); | |
}); | |
}, | |
getAttribute: function (name) { | |
var attr = this[0].getAttributeNode(name); | |
return attr && attr.specified ? attr.value : null; | |
}, | |
hasAttribute: function (name) { | |
var node = this[0]; | |
if (node.hasAttribute) | |
return node.hasAttribute(name); | |
var attr = node.getAttributeNode(name); | |
return !!(attr && attr.specified); | |
}, | |
removeAttribute: function (name) { | |
return this.forEach(function (node) { | |
var attr = node.getAttributeNode(name); | |
if (attr) | |
node.removeAttributeNode(attr); | |
}); | |
} | |
}); | |
var accessors = {}; | |
forEach([ | |
'type', | |
'value', | |
'name', | |
'href', | |
'title', | |
'id' | |
], function (name) { | |
accessors[name] = function (value) { | |
return value !== undefined ? this.forEach(function (node) { | |
node[name] = value; | |
}) : this[0][name]; | |
}; | |
}); | |
forEach([ | |
'checked', | |
'disabled', | |
'selected' | |
], function (name) { | |
accessors[name] = function (value) { | |
return value !== undefined ? this.forEach(function (node) { | |
node[name] = !!value; | |
}) : !!this[0][name]; | |
}; | |
}); | |
var classes = function (className) { | |
var classNames = trim(className).replace(/\s+/g, ' ').split(' '), uniques = {}; | |
return filter(classNames, function (className) { | |
if (className !== '' && !uniques[className]) | |
return uniques[className] = className; | |
}).sort(); | |
}; | |
accessors.className = function (className) { | |
return className !== undefined ? this.forEach(function (node) { | |
node.className = classes(className).join(' '); | |
}) : classes(this[0].className).join(' '); | |
}; | |
$.implement({ | |
attribute: function (name, value) { | |
var accessor = accessors[name]; | |
if (accessor) | |
return accessor.call(this, value); | |
if (value != null) | |
return this.setAttribute(name, value); | |
if (value === null) | |
return this.removeAttribute(name); | |
if (value === undefined) | |
return this.getAttribute(name); | |
} | |
}); | |
$.implement(accessors); | |
$.implement({ | |
check: function () { | |
return this.checked(true); | |
}, | |
uncheck: function () { | |
return this.checked(false); | |
}, | |
disable: function () { | |
return this.disabled(true); | |
}, | |
enable: function () { | |
return this.disabled(false); | |
}, | |
select: function () { | |
return this.selected(true); | |
}, | |
deselect: function () { | |
return this.selected(false); | |
} | |
}); | |
$.implement({ | |
classNames: function () { | |
return classes(this[0].className); | |
}, | |
hasClass: function (className) { | |
return indexOf(this.classNames(), className) > -1; | |
}, | |
addClass: function (className) { | |
return this.forEach(function (node) { | |
var nodeClassName = node.className; | |
var classNames = classes(nodeClassName + ' ' + className).join(' '); | |
if (nodeClassName !== classNames) | |
node.className = classNames; | |
}); | |
}, | |
removeClass: function (className) { | |
return this.forEach(function (node) { | |
var classNames = classes(node.className); | |
forEach(classes(className), function (className) { | |
var index = indexOf(classNames, className); | |
if (index > -1) | |
classNames.splice(index, 1); | |
}); | |
node.className = classNames.join(' '); | |
}); | |
} | |
}); | |
$.prototype.toString = function () { | |
var tag = this.tag(), id = this.id(), classes = this.classNames(); | |
var str = tag; | |
if (id) | |
str += '#' + id; | |
if (classes.length) | |
str += '.' + classes.join('.'); | |
return str; | |
}; | |
var textProperty = document.createElement('div').textContent == null ? 'innerText' : 'textContent'; | |
$.implement({ | |
tag: function () { | |
return this[0].tagName.toLowerCase(); | |
}, | |
html: function (html) { | |
return html !== undefined ? this.forEach(function (node) { | |
node.innerHTML = html; | |
}) : this[0].innerHTML; | |
}, | |
text: function (text) { | |
return text !== undefined ? this.forEach(function (node) { | |
node[textProperty] = text; | |
}) : this[0][textProperty]; | |
}, | |
data: function (key, value) { | |
switch (value) { | |
case undefined: | |
return this.getAttribute('data-' + key); | |
case null: | |
return this.removeAttribute('data-' + key); | |
default: | |
return this.setAttribute('data-' + key, value); | |
} | |
} | |
}); | |
module.exports = $; | |
}, | |
'node_modules/spotify-events/node_modules/elements/events.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var Emitter = require('node_modules/prime/emitter.js'); | |
var $ = require('node_modules/spotify-events/node_modules/elements/base.js'); | |
var html = document.documentElement; | |
var addEventListener = html.addEventListener ? function (node, event, handle, useCapture) { | |
node.addEventListener(event, handle, useCapture || false); | |
return handle; | |
} : function (node, event, handle) { | |
node.attachEvent('on' + event, handle); | |
return handle; | |
}; | |
var removeEventListener = html.removeEventListener ? function (node, event, handle, useCapture) { | |
node.removeEventListener(event, handle, useCapture || false); | |
} : function (node, event, handle) { | |
node.detachEvent('on' + event, handle); | |
}; | |
$.implement({ | |
on: function (event, handle, useCapture) { | |
return this.forEach(function (node) { | |
var self = $(node); | |
var internalEvent = event + (useCapture ? ':capture' : ''); | |
Emitter.prototype.on.call(self, internalEvent, handle); | |
var domListeners = self._domListeners || (self._domListeners = {}); | |
if (!domListeners[internalEvent]) | |
domListeners[internalEvent] = addEventListener(node, event, function (e) { | |
Emitter.prototype.emit.call(self, internalEvent, e || window.event, Emitter.EMIT_SYNC); | |
}, useCapture); | |
}); | |
}, | |
off: function (event, handle, useCapture) { | |
return this.forEach(function (node) { | |
var self = $(node); | |
var internalEvent = event + (useCapture ? ':capture' : ''); | |
var domListeners = self._domListeners, domEvent, listeners = self._listeners, events; | |
if (domListeners && (domEvent = domListeners[internalEvent]) && listeners && (events = listeners[internalEvent])) { | |
Emitter.prototype.off.call(self, internalEvent, handle); | |
if (!self._listeners || !self._listeners[event]) { | |
removeEventListener(node, event, domEvent); | |
delete domListeners[event]; | |
for (var l in domListeners) | |
return; | |
delete self._domListeners; | |
} | |
} | |
}); | |
}, | |
emit: function () { | |
var args = arguments; | |
return this.forEach(function (node) { | |
Emitter.prototype.emit.apply($(node), args); | |
}); | |
} | |
}); | |
module.exports = $; | |
}, | |
'node_modules/spotify-events/node_modules/elements/insertion.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var $ = require('node_modules/spotify-events/node_modules/elements/base.js'); | |
$.implement({ | |
appendChild: function (child) { | |
this[0].appendChild($(child)[0]); | |
return this; | |
}, | |
insertBefore: function (child, ref) { | |
this[0].insertBefore($(child)[0], $(ref)[0]); | |
return this; | |
}, | |
removeChild: function (child) { | |
this[0].removeChild($(child)[0]); | |
return this; | |
}, | |
replaceChild: function (child, ref) { | |
this[0].replaceChild($(child)[0], $(ref)[0]); | |
return this; | |
} | |
}); | |
$.implement({ | |
before: function (element) { | |
element = $(element)[0]; | |
var parent = element.parentNode; | |
if (parent) | |
this.forEach(function (node) { | |
parent.insertBefore(node, element); | |
}); | |
return this; | |
}, | |
after: function (element) { | |
element = $(element)[0]; | |
var parent = element.parentNode; | |
if (parent) | |
this.forEach(function (node) { | |
parent.insertBefore(node, element.nextSibling); | |
}); | |
return this; | |
}, | |
bottom: function (element) { | |
element = $(element)[0]; | |
return this.forEach(function (node) { | |
element.appendChild(node); | |
}); | |
}, | |
top: function (element) { | |
element = $(element)[0]; | |
return this.forEach(function (node) { | |
element.insertBefore(node, element.firstChild); | |
}); | |
} | |
}); | |
$.implement({ | |
insert: $.prototype.bottom, | |
remove: function () { | |
return this.forEach(function (node) { | |
var parent = node.parentNode; | |
if (parent) | |
parent.removeChild(node); | |
}); | |
}, | |
replace: function (element) { | |
element = $(element)[0]; | |
element.parentNode.replaceChild(this[0], element); | |
return this; | |
} | |
}); | |
module.exports = $; | |
}, | |
'node_modules/spotify-events/node_modules/elements/node_modules/slick/parser.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var escapeRe = /([-.*+?^${}()|[\]\/\\])/g, unescapeRe = /\\/g; | |
var escape = function (string) { | |
return (string + '').replace(escapeRe, '\\$1'); | |
}; | |
var unescape = function (string) { | |
return (string + '').replace(unescapeRe, ''); | |
}; | |
var slickRe = RegExp('^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:(["\']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:(["\'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)'.replace(/<combinator>/, '[' + escape('>+~`!@$%^&={}\\;</') + ']').replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])').replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')); | |
var Part = function Part(combinator) { | |
this.combinator = combinator || ' '; | |
this.tag = '*'; | |
}; | |
Part.prototype.toString = function () { | |
if (!this.raw) { | |
var xpr = '', k, part; | |
xpr += this.tag || '*'; | |
if (this.id) | |
xpr += '#' + this.id; | |
if (this.classes) | |
xpr += '.' + this.classList.join('.'); | |
if (this.attributes) | |
for (k = 0; part = this.attributes[k++];) { | |
xpr += '[' + part.name + (part.operator ? part.operator + '"' + part.value + '"' : '') + ']'; | |
} | |
if (this.pseudos) | |
for (k = 0; part = this.pseudos[k++];) { | |
xpr += ':' + part.name; | |
if (part.value) | |
xpr += '(' + part.value + ')'; | |
} | |
this.raw = xpr; | |
} | |
return this.raw; | |
}; | |
var Expression = function Expression() { | |
this.length = 0; | |
}; | |
Expression.prototype.toString = function () { | |
if (!this.raw) { | |
var xpr = ''; | |
for (var j = 0, bit; bit = this[j++];) { | |
if (j !== 1) | |
xpr += ' '; | |
if (bit.combinator !== ' ') | |
xpr += bit.combinator + ' '; | |
xpr += bit; | |
} | |
this.raw = xpr; | |
} | |
return this.raw; | |
}; | |
var replacer = function (rawMatch, separator, combinator, combinatorChildren, tagName, id, className, attributeKey, attributeOperator, attributeQuote, attributeValue, pseudoMarker, pseudoClass, pseudoQuote, pseudoClassQuotedValue, pseudoClassValue) { | |
var expression, current; | |
if (separator || !this.length) { | |
expression = this[this.length++] = new Expression(); | |
if (separator) | |
return ''; | |
} | |
if (!expression) | |
expression = this[this.length - 1]; | |
if (combinator || combinatorChildren || !expression.length) { | |
current = expression[expression.length++] = new Part(combinator); | |
} | |
if (!current) | |
current = expression[expression.length - 1]; | |
if (tagName) { | |
current.tag = unescape(tagName); | |
} else if (id) { | |
current.id = unescape(id); | |
} else if (className) { | |
var unescaped = unescape(className); | |
var classes = current.classes || (current.classes = {}); | |
if (!classes[unescaped]) { | |
classes[unescaped] = escape(className); | |
var classList = current.classList || (current.classList = []); | |
classList.push(unescaped); | |
classList.sort(); | |
} | |
} else if (pseudoClass) { | |
pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue; | |
(current.pseudos || (current.pseudos = [])).push({ | |
type: pseudoMarker.length == 1 ? 'class' : 'element', | |
name: unescape(pseudoClass), | |
escapedName: escape(pseudoClass), | |
value: pseudoClassValue ? unescape(pseudoClassValue) : null, | |
escapedValue: pseudoClassValue ? escape(pseudoClassValue) : null | |
}); | |
} else if (attributeKey) { | |
attributeValue = attributeValue ? escape(attributeValue) : null; | |
(current.attributes || (current.attributes = [])).push({ | |
operator: attributeOperator, | |
name: unescape(attributeKey), | |
escapedName: escape(attributeKey), | |
value: attributeValue ? unescape(attributeValue) : null, | |
escapedValue: attributeValue ? escape(attributeValue) : null | |
}); | |
} | |
return ''; | |
}; | |
var Expressions = function Expressions(expression) { | |
this.length = 0; | |
var self = this; | |
var original = expression, replaced; | |
while (expression) { | |
replaced = expression.replace(slickRe, function () { | |
return replacer.apply(self, arguments); | |
}); | |
if (replaced === expression) | |
throw new Error(original + ' is an invalid expression'); | |
expression = replaced; | |
} | |
}; | |
Expressions.prototype.toString = function () { | |
if (!this.raw) { | |
var expressions = []; | |
for (var i = 0, expression; expression = this[i++];) | |
expressions.push(expression); | |
this.raw = expressions.join(', '); | |
} | |
return this.raw; | |
}; | |
var cache = {}; | |
var parse = function (expression) { | |
if (expression == null) | |
return null; | |
expression = ('' + expression).replace(/^\s+|\s+$/g, ''); | |
return cache[expression] || (cache[expression] = new Expressions(expression)); | |
}; | |
module.exports = parse; | |
}, | |
'node_modules/spotify-events/node_modules/elements/node_modules/slick/finder.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var parse = require('node_modules/spotify-events/node_modules/elements/node_modules/slick/parser.js'); | |
var index = 0, counter = document.__counter = (parseInt(document.__counter || -1, 36) + 1).toString(36), key = 'uid:' + counter; | |
var uniqueID = function (n, xml) { | |
if (n === window) | |
return 'window'; | |
if (n === document) | |
return 'document'; | |
if (n === document.documentElement) | |
return 'html'; | |
if (xml) { | |
var uid = n.getAttribute(key); | |
if (!uid) { | |
uid = (index++).toString(36); | |
n.setAttribute(key, uid); | |
} | |
return uid; | |
} else { | |
return n[key] || (n[key] = (index++).toString(36)); | |
} | |
}; | |
var uniqueIDXML = function (n) { | |
return uniqueID(n, true); | |
}; | |
var isArray = Array.isArray || function (object) { | |
return Object.prototype.toString.call(object) === '[object Array]'; | |
}; | |
var uniqueIndex = 0; | |
var HAS = { | |
GET_ELEMENT_BY_ID: function (test, id) { | |
id = 'slick_' + uniqueIndex++; | |
test.innerHTML = '<a id="' + id + '"></a>'; | |
return !!this.getElementById(id); | |
}, | |
QUERY_SELECTOR: function (test) { | |
test.innerHTML = '_<style>:nth-child(2){}</style>'; | |
test.innerHTML = '<a class="MiX"></a>'; | |
return test.querySelectorAll('.MiX').length === 1; | |
}, | |
EXPANDOS: function (test, id) { | |
id = 'slick_' + uniqueIndex++; | |
test._custom_property_ = id; | |
return test._custom_property_ === id; | |
}, | |
MATCHES_SELECTOR: function (test) { | |
test.className = 'MiX'; | |
var matches = test.matchesSelector || test.mozMatchesSelector || test.webkitMatchesSelector; | |
if (matches) | |
try { | |
matches.call(test, ':slick'); | |
} catch (e) { | |
return matches.call(test, '.MiX') ? matches : false; | |
} | |
return false; | |
}, | |
GET_ELEMENTS_BY_CLASS_NAME: function (test) { | |
test.innerHTML = '<a class="f"></a><a class="b"></a>'; | |
if (test.getElementsByClassName('b').length !== 1) | |
return false; | |
test.firstChild.className = 'b'; | |
if (test.getElementsByClassName('b').length !== 2) | |
return false; | |
test.innerHTML = '<a class="a"></a><a class="f b a"></a>'; | |
if (test.getElementsByClassName('a').length !== 2) | |
return false; | |
return true; | |
}, | |
GET_ATTRIBUTE: function (test) { | |
var shout = 'fus ro dah'; | |
test.innerHTML = '<a class="' + shout + '"></a>'; | |
return test.firstChild.getAttribute('class') === shout; | |
} | |
}; | |
var Finder = function Finder(document) { | |
this.document = document; | |
var root = this.root = document.documentElement; | |
this.tested = {}; | |
this.uniqueID = this.has('EXPANDOS') ? uniqueID : uniqueIDXML; | |
this.getAttribute = this.has('GET_ATTRIBUTE') ? function (node, name) { | |
return node.getAttribute(name); | |
} : function (node, name) { | |
node = node.getAttributeNode(name); | |
return node && node.specified ? node.value : null; | |
}; | |
this.hasAttribute = root.hasAttribute ? function (node, attribute) { | |
return node.hasAttribute(attribute); | |
} : function (node, attribute) { | |
node = node.getAttributeNode(attribute); | |
return !!(node && node.specified); | |
}; | |
this.contains = document.contains && root.contains ? function (context, node) { | |
return context.contains(node); | |
} : root.compareDocumentPosition ? function (context, node) { | |
return context === node || !!(context.compareDocumentPosition(node) & 16); | |
} : function (context, node) { | |
do { | |
if (node === context) | |
return true; | |
} while (node = node.parentNode); | |
return false; | |
}; | |
this.sorter = root.compareDocumentPosition ? function (a, b) { | |
if (!a.compareDocumentPosition || !b.compareDocumentPosition) | |
return 0; | |
return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; | |
} : 'sourceIndex' in root ? function (a, b) { | |
if (!a.sourceIndex || !b.sourceIndex) | |
return 0; | |
return a.sourceIndex - b.sourceIndex; | |
} : document.createRange ? function (a, b) { | |
if (!a.ownerDocument || !b.ownerDocument) | |
return 0; | |
var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); | |
aRange.setStart(a, 0); | |
aRange.setEnd(a, 0); | |
bRange.setStart(b, 0); | |
bRange.setEnd(b, 0); | |
return aRange.compareBoundaryPoints(Range.START_TO_END, bRange); | |
} : null; | |
this.failed = {}; | |
var nativeMatches = this.has('MATCHES_SELECTOR'); | |
if (nativeMatches) | |
this.matchesSelector = function (node, expression) { | |
if (this.failed[expression]) | |
return null; | |
try { | |
return nativeMatches.call(node, expression); | |
} catch (e) { | |
if (slick.debug) | |
console.warn('matchesSelector failed on ' + expression); | |
this.failed[expression] = true; | |
return null; | |
} | |
}; | |
if (this.has('QUERY_SELECTOR')) { | |
this.querySelectorAll = function (node, expression) { | |
if (this.failed[expression]) | |
return true; | |
var result, _id, _expression, _combinator, _node; | |
if (node !== this.document) { | |
_combinator = expression[0].combinator; | |
_id = node.getAttribute('id'); | |
_expression = expression; | |
if (!_id) { | |
_node = node; | |
_id = '__slick__'; | |
_node.setAttribute('id', _id); | |
} | |
expression = '#' + _id + ' ' + _expression; | |
if (_combinator.indexOf('~') > -1 || _combinator.indexOf('+') > -1) { | |
node = node.parentNode; | |
if (!node) | |
result = true; | |
} | |
} | |
if (!result) | |
try { | |
result = node.querySelectorAll(expression.toString()); | |
} catch (e) { | |
if (slick.debug) | |
console.warn('querySelectorAll failed on ' + (_expression || expression)); | |
result = this.failed[_expression || expression] = true; | |
} | |
if (_node) | |
_node.removeAttribute('id'); | |
return result; | |
}; | |
} | |
}; | |
Finder.prototype.has = function (FEATURE) { | |
var tested = this.tested, testedFEATURE = tested[FEATURE]; | |
if (testedFEATURE != null) | |
return testedFEATURE; | |
var root = this.root, document = this.document, testNode = document.createElement('div'); | |
testNode.setAttribute('style', 'display: none;'); | |
root.appendChild(testNode); | |
var TEST = HAS[FEATURE], result = false; | |
if (TEST) | |
try { | |
result = TEST.call(document, testNode); | |
} catch (e) { | |
} | |
if (slick.debug && !result) | |
console.warn('document has no ' + FEATURE); | |
root.removeChild(testNode); | |
return tested[FEATURE] = result; | |
}; | |
var combinators = { | |
' ': function (node, part, push) { | |
var item, items; | |
var noId = !part.id, noTag = !part.tag, noClass = !part.classes; | |
if (part.id && node.getElementById && this.has('GET_ELEMENT_BY_ID')) { | |
item = node.getElementById(part.id); | |
if (item && item.getAttribute('id') === part.id) { | |
items = [item]; | |
noId = true; | |
if (part.tag === '*') | |
noTag = true; | |
} | |
} | |
if (!items) { | |
if (part.classes && node.getElementsByClassName && this.has('GET_ELEMENTS_BY_CLASS_NAME')) { | |
items = node.getElementsByClassName(part.classList); | |
noClass = true; | |
if (part.tag === '*') | |
noTag = true; | |
} else { | |
items = node.getElementsByTagName(part.tag); | |
if (part.tag !== '*') | |
noTag = true; | |
} | |
if (!items || !items.length) | |
return false; | |
} | |
for (var i = 0; item = items[i++];) | |
if (noTag && noId && noClass && !part.attributes && !part.pseudos || this.match(item, part, noTag, noId, noClass)) | |
push(item); | |
return true; | |
}, | |
'>': function (node, part, push) { | |
if (node = node.firstChild) | |
do { | |
if (node.nodeType == 1 && this.match(node, part)) | |
push(node); | |
} while (node = node.nextSibling); | |
}, | |
'+': function (node, part, push) { | |
while (node = node.nextSibling) | |
if (node.nodeType == 1) { | |
if (this.match(node, part)) | |
push(node); | |
break; | |
} | |
}, | |
'^': function (node, part, push) { | |
node = node.firstChild; | |
if (node) { | |
if (node.nodeType === 1) { | |
if (this.match(node, part)) | |
push(node); | |
} else { | |
combinators['+'].call(this, node, part, push); | |
} | |
} | |
}, | |
'~': function (node, part, push) { | |
while (node = node.nextSibling) { | |
if (node.nodeType === 1 && this.match(node, part)) | |
push(node); | |
} | |
}, | |
'++': function (node, part, push) { | |
combinators['+'].call(this, node, part, push); | |
combinators['!+'].call(this, node, part, push); | |
}, | |
'~~': function (node, part, push) { | |
combinators['~'].call(this, node, part, push); | |
combinators['!~'].call(this, node, part, push); | |
}, | |
'!': function (node, part, push) { | |
while (node = node.parentNode) | |
if (node !== this.document && this.match(node, part)) | |
push(node); | |
}, | |
'!>': function (node, part, push) { | |
node = node.parentNode; | |
if (node !== this.document && this.match(node, part)) | |
push(node); | |
}, | |
'!+': function (node, part, push) { | |
while (node = node.previousSibling) | |
if (node.nodeType == 1) { | |
if (this.match(node, part)) | |
push(node); | |
break; | |
} | |
}, | |
'!^': function (node, part, push) { | |
node = node.lastChild; | |
if (node) { | |
if (node.nodeType == 1) { | |
if (this.match(node, part)) | |
push(node); | |
} else { | |
combinators['!+'].call(this, node, part, push); | |
} | |
} | |
}, | |
'!~': function (node, part, push) { | |
while (node = node.previousSibling) { | |
if (node.nodeType === 1 && this.match(node, part)) | |
push(node); | |
} | |
} | |
}; | |
Finder.prototype.search = function (context, expression, found) { | |
if (!context) | |
context = this.document; | |
else if (!context.nodeType && context.document) | |
context = context.document; | |
var expressions = parse(expression); | |
if (!expressions || !expressions.length) | |
throw new Error('invalid expression'); | |
if (!found) | |
found = []; | |
var uniques, push = isArray(found) ? function (node) { | |
found[found.length] = node; | |
} : function (node) { | |
found[found.length++] = node; | |
}; | |
if (expressions.length > 1) { | |
uniques = {}; | |
var plush = push; | |
push = function (node) { | |
var uid = uniqueID(node); | |
if (!uniques[uid]) { | |
uniques[uid] = true; | |
plush(node); | |
} | |
}; | |
} | |
var node, nodes, part; | |
main: | |
for (var i = 0; expression = expressions[i++];) { | |
if (!slick.noQSA && this.querySelectorAll) { | |
nodes = this.querySelectorAll(context, expression); | |
if (nodes !== true) { | |
if (nodes && nodes.length) | |
for (var j = 0; node = nodes[j++];) | |
if (node.nodeName > '@') { | |
push(node); | |
} | |
continue main; | |
} | |
} | |
if (expression.length === 1) { | |
part = expression[0]; | |
combinators[part.combinator].call(this, context, part, push); | |
} else { | |
var cs = [context], c, f, u, p = function (node) { | |
var uid = uniqueID(node); | |
if (!u[uid]) { | |
u[uid] = true; | |
f[f.length] = node; | |
} | |
}; | |
for (var j = 0; part = expression[j++];) { | |
f = []; | |
u = {}; | |
for (var k = 0; c = cs[k++];) | |
combinators[part.combinator].call(this, c, part, p); | |
if (!f.length) | |
continue main; | |
cs = f; | |
} | |
if (i === 0) | |
found = f; | |
else | |
for (var l = 0; l < f.length; l++) | |
push(f[l]); | |
} | |
} | |
if (uniques && found && found.length > 1) | |
this.sort(found); | |
return found; | |
}; | |
Finder.prototype.sort = function (nodes) { | |
return this.sorter ? Array.prototype.sort.call(nodes, this.sorter) : nodes; | |
}; | |
var pseudos = { | |
'empty': function () { | |
return !(this && this.nodeType === 1) && !(this.innerText || this.textContent || '').length; | |
}, | |
'not': function (expression) { | |
return !slick.match(this, expression); | |
}, | |
'contains': function (text) { | |
return (this.innerText || this.textContent || '').indexOf(text) > -1; | |
}, | |
'first-child': function () { | |
var node = this; | |
while (node = node.previousSibling) | |
if (node.nodeType == 1) | |
return false; | |
return true; | |
}, | |
'last-child': function () { | |
var node = this; | |
while (node = node.nextSibling) | |
if (node.nodeType == 1) | |
return false; | |
return true; | |
}, | |
'only-child': function () { | |
var prev = this; | |
while (prev = prev.previousSibling) | |
if (prev.nodeType == 1) | |
return false; | |
var next = this; | |
while (next = next.nextSibling) | |
if (next.nodeType == 1) | |
return false; | |
return true; | |
}, | |
'first-of-type': function () { | |
var node = this, nodeName = node.nodeName; | |
while (node = node.previousSibling) | |
if (node.nodeName == nodeName) | |
return false; | |
return true; | |
}, | |
'last-of-type': function () { | |
var node = this, nodeName = node.nodeName; | |
while (node = node.nextSibling) | |
if (node.nodeName == nodeName) | |
return false; | |
return true; | |
}, | |
'only-of-type': function () { | |
var prev = this, nodeName = this.nodeName; | |
while (prev = prev.previousSibling) | |
if (prev.nodeName == nodeName) | |
return false; | |
var next = this; | |
while (next = next.nextSibling) | |
if (next.nodeName == nodeName) | |
return false; | |
return true; | |
}, | |
'enabled': function () { | |
return !this.disabled; | |
}, | |
'disabled': function () { | |
return this.disabled; | |
}, | |
'checked': function () { | |
return this.checked || this.selected; | |
}, | |
'selected': function () { | |
return this.selected; | |
}, | |
'focus': function () { | |
var doc = this.ownerDocument; | |
return doc.activeElement === this && (this.href || this.type || slick.hasAttribute(this, 'tabindex')); | |
}, | |
'root': function () { | |
return this === this.ownerDocument.documentElement; | |
} | |
}; | |
Finder.prototype.match = function (node, bit, noTag, noId, noClass) { | |
if (!slick.noQSA && this.matchesSelector) { | |
var matches = this.matchesSelector(node, bit); | |
if (matches !== null) | |
return matches; | |
} | |
if (!noTag && bit.tag) { | |
var nodeName = node.nodeName.toLowerCase(); | |
if (bit.tag === '*') { | |
if (nodeName < '@') | |
return false; | |
} else if (nodeName != bit.tag) { | |
return false; | |
} | |
} | |
if (!noId && bit.id && node.getAttribute('id') !== bit.id) | |
return false; | |
var i, part; | |
if (!noClass && bit.classes) { | |
var className = this.getAttribute(node, 'class'); | |
if (!className) | |
return false; | |
for (part in bit.classes) | |
if (!RegExp('(^|\\s)' + bit.classes[part] + '(\\s|$)').test(className)) | |
return false; | |
} | |
var name, value; | |
if (bit.attributes) | |
for (i = 0; part = bit.attributes[i++];) { | |
var operator = part.operator, escaped = part.escapedValue; | |
name = part.name; | |
value = part.value; | |
if (!operator) { | |
if (!this.hasAttribute(node, name)) | |
return false; | |
} else { | |
var actual = this.getAttribute(node, name); | |
if (actual == null) | |
return false; | |
switch (operator) { | |
case '^=': | |
if (!RegExp('^' + escaped).test(actual)) | |
return false; | |
break; | |
case '$=': | |
if (!RegExp(escaped + '$').test(actual)) | |
return false; | |
break; | |
case '~=': | |
if (!RegExp('(^|\\s)' + escaped + '(\\s|$)').test(actual)) | |
return false; | |
break; | |
case '|=': | |
if (!RegExp('^' + escaped + '(-|$)').test(actual)) | |
return false; | |
break; | |
case '=': | |
if (actual !== value) | |
return false; | |
break; | |
case '*=': | |
if (actual.indexOf(value) === -1) | |
return false; | |
break; | |
default: | |
return false; | |
} | |
} | |
} | |
if (bit.pseudos) | |
for (i = 0; part = bit.pseudos[i++];) { | |
name = part.name; | |
value = part.value; | |
if (pseudos[name]) | |
return pseudos[name].call(node, value); | |
if (value != null) { | |
if (this.getAttribute(node, name) !== value) | |
return false; | |
} else { | |
if (!this.hasAttribute(node, name)) | |
return false; | |
} | |
} | |
return true; | |
}; | |
Finder.prototype.matches = function (node, expression) { | |
var expressions = parse(expression); | |
if (expressions.length === 1 && expressions[0].length === 1) { | |
return this.match(node, expressions[0][0]); | |
} | |
if (!slick.noQSA && this.matchesSelector) { | |
var matches = this.matchesSelector(node, expressions); | |
if (matches !== null) | |
return matches; | |
} | |
var nodes = this.search(this.document, expression, { length: 0 }); | |
for (var i = 0, res; res = nodes[i++];) | |
if (node === res) | |
return true; | |
return false; | |
}; | |
var finders = {}; | |
var finder = function (context) { | |
var doc = context || document; | |
if (doc.ownerDocument) | |
doc = doc.ownerDocument; | |
else if (doc.document) | |
doc = doc.document; | |
if (doc.nodeType !== 9) | |
throw new TypeError('invalid document'); | |
var uid = uniqueID(doc); | |
return finders[uid] || (finders[uid] = new Finder(doc)); | |
}; | |
var slick = function (expression, context) { | |
return slick.search(expression, context); | |
}; | |
slick.search = function (expression, context, found) { | |
return finder(context).search(context, expression, found); | |
}; | |
slick.find = function (expression, context) { | |
return finder(context).search(context, expression)[0] || null; | |
}; | |
slick.getAttribute = function (node, name) { | |
return finder(node).getAttribute(node, name); | |
}; | |
slick.hasAttribute = function (node, name) { | |
return finder(node).hasAttribute(node, name); | |
}; | |
slick.contains = function (context, node) { | |
return finder(context).contains(context, node); | |
}; | |
slick.matches = function (node, expression) { | |
return finder(node).matches(node, expression); | |
}; | |
slick.sort = function (nodes) { | |
if (nodes && nodes.length > 1) | |
finder(nodes[0]).sort(nodes); | |
return nodes; | |
}; | |
slick.parse = parse; | |
module.exports = slick; | |
}, | |
'node_modules/spotify-events/node_modules/elements/node_modules/slick/index.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
module.exports = 'document' in global ? require('node_modules/spotify-events/node_modules/elements/node_modules/slick/finder.js') : { parse: require('node_modules/spotify-events/node_modules/elements/node_modules/slick/parser.js') }; | |
}, | |
'node_modules/spotify-events/node_modules/elements/traversal.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var map = require('node_modules/mout/array/map.js'); | |
var slick = require('node_modules/spotify-events/node_modules/elements/node_modules/slick/index.js'); | |
var $ = require('node_modules/spotify-events/node_modules/elements/base.js'); | |
var gen = function (combinator, expression) { | |
return map(slick.parse(expression || '*'), function (part) { | |
return combinator + ' ' + part; | |
}).join(', '); | |
}; | |
var push_ = Array.prototype.push; | |
$.implement({ | |
search: function (expression) { | |
if (this.length === 1) | |
return $(slick.search(expression, this[0], new $())); | |
var buffer = []; | |
for (var i = 0, node; node = this[i]; i++) | |
push_.apply(buffer, slick.search(expression, node)); | |
buffer = $(buffer); | |
return buffer && buffer.sort(); | |
}, | |
find: function (expression) { | |
if (this.length === 1) | |
return $(slick.find(expression, this[0])); | |
for (var i = 0, node; node = this[i]; i++) { | |
var found = slick.find(expression, node); | |
if (found) | |
return $(found); | |
} | |
return null; | |
}, | |
sort: function () { | |
return slick.sort(this); | |
}, | |
matches: function (expression) { | |
return slick.matches(this[0], expression); | |
}, | |
contains: function (node) { | |
return slick.contains(this[0], node); | |
}, | |
nextSiblings: function (expression) { | |
return this.search(gen('~', expression)); | |
}, | |
nextSibling: function (expression) { | |
return this.find(gen('+', expression)); | |
}, | |
previousSiblings: function (expression) { | |
return this.search(gen('!~', expression)); | |
}, | |
previousSibling: function (expression) { | |
return this.find(gen('!+', expression)); | |
}, | |
children: function (expression) { | |
return this.search(gen('>', expression)); | |
}, | |
firstChild: function (expression) { | |
return this.find(gen('^', expression)); | |
}, | |
lastChild: function (expression) { | |
return this.find(gen('!^', expression)); | |
}, | |
parent: function (expression) { | |
var buffer = []; | |
loop: | |
for (var i = 0, node; node = this[i]; i++) | |
while ((node = node.parentNode) && node !== document) { | |
if (!expression || slick.matches(node, expression)) { | |
buffer.push(node); | |
break loop; | |
break; | |
} | |
} | |
return $(buffer); | |
}, | |
parents: function (expression) { | |
var buffer = []; | |
for (var i = 0, node; node = this[i]; i++) | |
while ((node = node.parentNode) && node !== document) { | |
if (!expression || slick.matches(node, expression)) | |
buffer.push(node); | |
} | |
return $(buffer); | |
} | |
}); | |
module.exports = $; | |
}, | |
'node_modules/spotify-events/node_modules/elements/delegation.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var Map = require('node_modules/prime/map.js'); | |
var $ = require('node_modules/spotify-events/node_modules/elements/events.js'); | |
require('node_modules/spotify-events/node_modules/elements/traversal.js'); | |
$.implement({ | |
delegate: function (event, selector, handle) { | |
return this.forEach(function (node) { | |
var self = $(node); | |
var delegation = self._delegation || (self._delegation = {}), events = delegation[event] || (delegation[event] = {}), map = events[selector] || (events[selector] = new Map()); | |
if (map.get(handle)) | |
return; | |
var action = function (e) { | |
var target = $(e.target || e.srcElement), match = target.matches(selector) ? target : target.parent(selector); | |
var res; | |
if (match) | |
res = handle.call(self, e, match); | |
return res; | |
}; | |
map.set(handle, action); | |
self.on(event, action); | |
}); | |
}, | |
undelegate: function (event, selector, handle) { | |
return this.forEach(function (node) { | |
var self = $(node), delegation, events, map; | |
if (!(delegation = self._delegation) || !(events = delegation[event]) || !(map = events[selector])) | |
return; | |
var action = map.get(handle); | |
if (action) { | |
self.off(event, action); | |
map.remove(action); | |
if (!map.count()) | |
delete events[selector]; | |
var e1 = true, e2 = true, x; | |
for (x in events) { | |
e1 = false; | |
break; | |
} | |
if (e1) | |
delete delegation[event]; | |
for (x in delegation) { | |
e2 = false; | |
break; | |
} | |
if (e2) | |
delete self._delegation; | |
} | |
}); | |
} | |
}); | |
module.exports = $; | |
}, | |
'node_modules/spotify-events/node_modules/elements/index.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var $ = require('node_modules/spotify-events/node_modules/elements/base.js'); | |
require('node_modules/spotify-events/node_modules/elements/attributes.js'); | |
require('node_modules/spotify-events/node_modules/elements/events.js'); | |
require('node_modules/spotify-events/node_modules/elements/insertion.js'); | |
require('node_modules/spotify-events/node_modules/elements/traversal.js'); | |
require('node_modules/spotify-events/node_modules/elements/delegation.js'); | |
module.exports = $; | |
}, | |
'node_modules/spotify-events/add.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var $ = require('node_modules/spotify-events/node_modules/elements/index.js'); | |
var forIn = require('node_modules/mout/object/forIn.js'); | |
var live = require('node_modules/spotify-live/index.js'); | |
var center = require('node_modules/spotify-events/center.js'); | |
var getAddType = require('node_modules/spotify-events/util/type.js'); | |
var IS_FOLLOWING_PROPERTY = 'isFollowing'; | |
var IS_IN_COLLECTION_PROPERTY = 'isInCollection'; | |
var FOLLOWERSCOUNT_PROPERTY = 'followersCount'; | |
var FOLLOWINGCOUNT_PROPERTY = 'followingCount'; | |
var BUTTON_SELECTOR = '[data-button=add]'; | |
var JUSTCHANGED_CLASSNAME = 'just-changed'; | |
var ADDED_CLASSNAME = 'added'; | |
var dataPropertiesForAdd = [ | |
IS_FOLLOWING_PROPERTY, | |
IS_IN_COLLECTION_PROPERTY, | |
FOLLOWERSCOUNT_PROPERTY, | |
FOLLOWINGCOUNT_PROPERTY | |
]; | |
var changeHandlers = {}; | |
var utils = { | |
getAddedClass: function (node) { | |
var addedClassNode = node.hasAttribute('data-class-added') ? node : node.parent('[data-class-added]'); | |
return addedClassNode ? addedClassNode.data('class-added') : ADDED_CLASSNAME; | |
}, | |
getURIFromNode: function (node) { | |
var stateNode = node.hasAttribute('data-uri') ? node : node.parent('[data-uri]'); | |
if (!stateNode) | |
return null; | |
return stateNode.data('uri'); | |
}, | |
getPropertyFromURI: function (uri) { | |
switch (getAddType(uri)) { | |
case 'follow': | |
return IS_FOLLOWING_PROPERTY; | |
case 'collection': | |
return IS_IN_COLLECTION_PROPERTY; | |
} | |
return null; | |
} | |
}; | |
var eventHandlers = { | |
click: function (event, node) { | |
var stateNode = node.hasAttribute('data-uri') ? node : node.parent('[data-uri]'); | |
if (!stateNode) | |
return; | |
var uri = stateNode.data('uri'); | |
if (!uri) | |
return; | |
var property = utils.getPropertyFromURI(uri); | |
if (property) { | |
var data = {}; | |
var addedClass = utils.getAddedClass(node); | |
var shouldBeAdded = !stateNode.hasClass(addedClass); | |
data[property] = shouldBeAdded; | |
if (shouldBeAdded) { | |
node.addClass(JUSTCHANGED_CLASSNAME); | |
node.on('mouseout', function mouseoutHandler(event) { | |
if (event.target !== node[0]) | |
return; | |
node.off('mouseout', mouseoutHandler); | |
node.removeClass(JUSTCHANGED_CLASSNAME); | |
}); | |
} | |
live(uri).publish(data); | |
} | |
}, | |
changeHandler: function (key, value, uri) { | |
if (key === IS_FOLLOWING_PROPERTY || key === IS_IN_COLLECTION_PROPERTY) { | |
var numAffectedButtons = actions.setStateForURI(uri, value); | |
if (numAffectedButtons === 0) { | |
live(uri).off('update', changeHandlers[uri]); | |
delete changeHandlers[uri]; | |
} | |
} | |
if (dataPropertiesForAdd.indexOf(key) > -1) { | |
center.emit('add-data-change', { | |
uri: uri, | |
model: live(uri), | |
key: key, | |
value: value | |
}); | |
} | |
} | |
}; | |
var actions = { | |
setStateForURI: function (uri, isAdded) { | |
var numAffectedButtons = 0; | |
var nodes = $(document).search(BUTTON_SELECTOR); | |
if (!nodes) | |
return numAffectedButtons; | |
for (var i = 0, l = nodes.length; i < l; i++) { | |
var node = $(nodes[i]); | |
var buttonURI = utils.getURIFromNode(node); | |
if (uri === buttonURI) { | |
numAffectedButtons++; | |
actions.setStateForButtonNode(node, isAdded); | |
} | |
} | |
return numAffectedButtons; | |
}, | |
setStateForButtonNode: function (node, isAdded) { | |
var stateNode = node.hasAttribute('data-uri') ? node : node.parent('[data-uri]'); | |
if (!stateNode) | |
return; | |
var addedClass = utils.getAddedClass(node); | |
actions.setVisualState(isAdded, stateNode, node, addedClass); | |
}, | |
setVisualState: function (isAdded, node, button, className) { | |
if (isAdded) { | |
if (!node.hasClass(className)) | |
node.addClass(className); | |
var tooltipRemove = button.data('tooltip-remove'); | |
if (tooltipRemove) { | |
button.setAttribute('data-tooltip', tooltipRemove); | |
} | |
} else { | |
if (node.hasClass(className)) | |
node.removeClass(className); | |
var tooltipAdd = button.data('tooltip-add'); | |
if (tooltipAdd) { | |
button.setAttribute('data-tooltip', tooltipAdd); | |
} | |
} | |
}, | |
addListenerForNode: function (node) { | |
var uri = utils.getURIFromNode(node); | |
var addedProperty = utils.getPropertyFromURI(uri); | |
if (changeHandlers[uri]) { | |
live(uri).get(addedProperty, function (error, isAdded) { | |
if (error) | |
throw error; | |
actions.setStateForButtonNode(node, isAdded); | |
}); | |
return; | |
} | |
changeHandlers[uri] = function (data) { | |
forIn(data, function (value, key) { | |
eventHandlers.changeHandler(key, value, uri); | |
}); | |
}; | |
var model = live(uri); | |
model.get(addedProperty, function (error, isAdded) { | |
if (error) | |
throw error; | |
actions.setStateForButtonNode(node, isAdded); | |
model.on('update', changeHandlers[uri]); | |
}); | |
} | |
}; | |
var handleScrollShow = function (data) { | |
update(data.pageNode, data.nodes); | |
}; | |
exports._classNames = { | |
ADDED: ADDED_CLASSNAME, | |
JUSTCHANGED: JUSTCHANGED_CLASSNAME | |
}; | |
exports.attach = function attach() { | |
$(document).delegate('click', BUTTON_SELECTOR, eventHandlers.click); | |
center.on('scroll-show-before', handleScrollShow); | |
}; | |
exports.detach = function detach() { | |
$(document).undelegate('click', BUTTON_SELECTOR, eventHandlers.click); | |
center.off('scroll-show-before', handleScrollShow); | |
}; | |
var update = exports.update = function update(node, nodes) { | |
if (node && $(node).matches(BUTTON_SELECTOR)) { | |
actions.addListenerForNode($(node)); | |
} else { | |
var nodes = ($(nodes) || $(node) || $(document)).search(BUTTON_SELECTOR); | |
if (!nodes) | |
return; | |
for (var i = 0, l = nodes.length; i < l; i++) { | |
actions.addListenerForNode($(nodes[i])); | |
} | |
} | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/src/message.js': function (require, module, exports, global, __filename, __dirname) { | |
exports.Headers; | |
exports.Body; | |
exports.SerializedMessage; | |
function Message(uri, opt_headers, opt_body) { | |
if (uri == null) | |
throw new TypeError('Invalid `uri` argument for Message.'); | |
this._uri = uri; | |
this._headers = {}; | |
this._body = this._encodeBody(opt_body || ''); | |
if (opt_headers) | |
this._setHeaders(opt_headers); | |
} | |
exports.Message = Message; | |
Message.fromObject = function (object) { | |
return object && object.uri ? new Message(object.uri, object.headers, object.body) : null; | |
}; | |
Message.prototype._encodeBody = function (body) { | |
if (typeof body != 'string') { | |
body = JSON.stringify(body); | |
} | |
return body; | |
}; | |
Message.prototype.getURI = function () { | |
return this._uri; | |
}; | |
Message.prototype.getMimeType = function () { | |
return this._headers['accept']; | |
}; | |
Message.prototype.getHeader = function (name) { | |
return this._headers[name.toLowerCase()] || null; | |
}; | |
Message.prototype.getHeaders = function () { | |
var _headers = this._headers; | |
var headers = {}; | |
for (var name in _headers) { | |
if (!_headers.hasOwnProperty(name)) | |
continue; | |
headers[name] = _headers[name]; | |
} | |
return headers; | |
}; | |
Message.prototype._setHeaders = function (headers) { | |
var _headers = this._headers; | |
for (var name in headers) { | |
if (!headers.hasOwnProperty(name)) | |
continue; | |
_headers[name.toLowerCase()] = headers[name]; | |
} | |
return this; | |
}; | |
Message.prototype.getBody = function () { | |
return this._body; | |
}; | |
Message.prototype.getJSONBody = function () { | |
try { | |
return JSON.parse(this._body); | |
} catch (e) { | |
return null; | |
} | |
}; | |
Message.prototype.copy = function (opt_headers, opt_body) { | |
return new Message(this._uri, this._copyHeaders(opt_headers), typeof opt_body != 'undefined' ? opt_body : this._body); | |
}; | |
Message.prototype._copyHeaders = function (opt_headers) { | |
var headers; | |
if (opt_headers) { | |
var _headers = this._headers; | |
var name; | |
headers = {}; | |
for (name in _headers) { | |
if (!_headers.hasOwnProperty(name)) | |
continue; | |
headers[name] = _headers[name]; | |
} | |
for (name in opt_headers) { | |
if (!opt_headers.hasOwnProperty(name)) | |
continue; | |
headers[name.toLowerCase()] = opt_headers[name]; | |
} | |
} else { | |
headers = this._headers; | |
} | |
return headers; | |
}; | |
Message.prototype.serialize = function () { | |
return this.toJSON(); | |
}; | |
Message.prototype.toJSON = function () { | |
return { | |
uri: this._uri, | |
headers: this._headers, | |
body: this._body | |
}; | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/environment.js': function (require, module, exports, global, __filename, __dirname) { | |
var windowMock = { | |
addEventListener: function () { | |
}, | |
postMessage: function () { | |
}, | |
location: { | |
hostname: '', | |
origin: '', | |
protocol: '' | |
} | |
}; | |
exports.environment = global.window || windowMock; | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/postrouter.js': function (require, module, exports, global, __filename, __dirname) { | |
exports.MessageHandler; | |
var env = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/environment.js').environment; | |
var handlers = {}; | |
function routeMessage(event) { | |
var data = event.data; | |
if (typeof data == 'string') { | |
try { | |
data = JSON.parse(data); | |
} catch (e) { | |
return; | |
} | |
} | |
var handler = handlers[data.type]; | |
if (!handler) | |
return; | |
handler.call(this, event, data); | |
} | |
if (env.attachEvent && !env.addEventListener) { | |
env.attachEvent('onmessage', routeMessage); | |
} else { | |
env.addEventListener('message', routeMessage, false); | |
} | |
exports.addMessageHandler = function (type, fn) { | |
if (handlers[type]) | |
throw new Error('Rehandling of message "' + type + '" not allowed.'); | |
handlers[type] = fn; | |
return; | |
}; | |
exports.removeMessageHandler = function (type, fn) { | |
if (handlers[type] == fn) { | |
handlers[type] = null; | |
return true; | |
} | |
return false; | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/defer.js': function (require, module, exports, global, __filename, __dirname) { | |
var postrouter = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/postrouter.js'); | |
var env = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/environment.js').environment; | |
var deferred = []; | |
var send = env.postMessage.bind(env, '{"type": "execute_deferreds"}', env.location.origin || [ | |
env.location.protocol, | |
env.location.hostname | |
].join('//')); | |
function executeDeferreds() { | |
var fns = deferred.splice(0); | |
if (!fns.length) | |
return; | |
for (var i = 0, l = fns.length; i < l; i++) { | |
var retry = true; | |
try { | |
fns[i](); | |
retry = false; | |
} finally { | |
if (retry) { | |
var trigger = !deferred.length; | |
deferred = fns.slice(++i).concat(deferred); | |
if (trigger) | |
send(); | |
} | |
} | |
} | |
} | |
postrouter.addMessageHandler('execute_deferreds', executeDeferreds); | |
exports.defer = function (fn) { | |
var trigger = !deferred.length; | |
deferred.push(fn); | |
if (trigger) | |
send(); | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/helpers.js': function (require, module, exports, global, __filename, __dirname) { | |
exports.shallowCopy = function (obj) { | |
function Object() { | |
} | |
Object.prototype = obj; | |
return new Object(); | |
}; | |
exports.inherit = function (Sub, Super) { | |
var superProto = Super.prototype; | |
function Superclass() { | |
} | |
Superclass.prototype = Sub._super = superProto; | |
Superclass.prototype.constructor = Super; | |
Sub.prototype = new Superclass(); | |
Sub.prototype._super = function (fn, args) { | |
args = typeof args == 'arguments' || Array.isArray(args) ? args : slice.call(arguments, 1); | |
return superProto[fn].apply(this, args); | |
}; | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/promise.js': function (require, module, exports, global, __filename, __dirname) { | |
var defer = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/defer.js').defer; | |
var toString = Object.prototype.toString; | |
var slice = Array.prototype.slice; | |
function pipe(promiseA, promiseB) { | |
promiseA.then(function (object) { | |
promiseB.fulfill(object); | |
}, function (error) { | |
promiseB.fail(error); | |
}); | |
} | |
var states = { | |
UNFULFILLED: 0, | |
FULFILLED: 1, | |
FAILED: 2 | |
}; | |
function Promise() { | |
this._state = states.UNFULFILLED; | |
this._value = null; | |
this._handlers = []; | |
this._deferred = false; | |
} | |
exports.Promise = Promise; | |
Promise.defer = typeof setImmediate == 'function' ? function (fn) { | |
setImmediate(fn); | |
} : defer || function (fn) { | |
setTimeout(fn, 0); | |
}; | |
Promise.prototype.isUnfulfilled = function () { | |
return this._state == states.UNFULFILLED; | |
}; | |
Promise.prototype.isFulfilled = function () { | |
return this._state == states.FULFILLED; | |
}; | |
Promise.prototype.isFailed = function () { | |
return this._state == states.FAILED; | |
}; | |
Promise.prototype.fulfill = function (value) { | |
var self = this; | |
if (this._state !== states.UNFULFILLED) | |
return; | |
this._value = value; | |
this._state = states.FULFILLED; | |
if (!this._deferred) { | |
Promise.defer(function () { | |
self._runHandlers(); | |
}); | |
this._deferred = true; | |
} | |
return; | |
}; | |
Promise.prototype.fail = function (error) { | |
var self = this; | |
if (this._state !== states.UNFULFILLED) | |
return; | |
this._value = error; | |
this._state = states.FAILED; | |
if (!this._deferred) { | |
Promise.defer(function () { | |
self._runHandlers(); | |
}); | |
this._deferred = true; | |
} | |
return; | |
}; | |
Promise.prototype.then = function (fulfilledHandler, failedHandler) { | |
var self = this; | |
var promise = new Promise(); | |
this._handlers.push({ | |
fulfilled: fulfilledHandler, | |
failed: failedHandler, | |
promise: promise | |
}); | |
if (this._state !== states.UNFULFILLED && !this._deferred) { | |
Promise.defer(function () { | |
self._runHandlers(); | |
}); | |
this._deferred = true; | |
} | |
return promise; | |
}; | |
Promise.prototype.pipe = function (promise) { | |
this.then(function (value) { | |
promise.fulfill(value); | |
}, function (error) { | |
promise.fail(error); | |
}); | |
return; | |
}; | |
Promise.prototype._runHandlers = function () { | |
this._deferred = false; | |
var value = this._value; | |
if (this._state == states.UNFULFILLED) | |
return; | |
var fulfilled = this._state === states.FULFILLED; | |
var handlers = this._handlers.splice(0); | |
for (var i = 0, l = handlers.length; i < l; i++) { | |
var handler = handlers[i]; | |
var callback = handler[fulfilled ? 'fulfilled' : 'failed']; | |
var promise = handler.promise; | |
if (!callback || typeof callback != 'function') { | |
if (value && typeof value.then == 'function') { | |
pipe(value, promise); | |
continue; | |
} else { | |
if (fulfilled) { | |
promise.fulfill(value); | |
} else { | |
promise.fail(value); | |
} | |
continue; | |
} | |
} | |
try { | |
var returnValue = callback(value); | |
} catch (e) { | |
promise.fail(e); | |
continue; | |
} | |
if (returnValue && typeof returnValue.then == 'function') { | |
pipe(returnValue, promise); | |
} else { | |
promise.fulfill(returnValue); | |
} | |
} | |
}; | |
Promise.prototype.catchError = function (failedHandler) { | |
return this.then(null, failedHandler); | |
}; | |
Promise.prototype.get = function (property) { | |
var promise = new Promise(); | |
this.then(function (object) { | |
promise.fulfill(object[property]); | |
}, function (error) { | |
promise.fail(error); | |
}); | |
return promise; | |
}; | |
Promise.prototype.call = function (method, var_args) { | |
var args = slice.call(arguments, 1); | |
var promise = new Promise(); | |
this.then(function (object) { | |
try { | |
promise.fulfill(object[method].apply(object, args)); | |
} catch (e) { | |
promise.fail(e); | |
} | |
}, function (error) { | |
promise.fail(error); | |
}); | |
return promise; | |
}; | |
Promise.prototype.thenSpread = function (fulfilledHandler, failedHandler) { | |
return this.then(function (value) { | |
return Array.isArray(value) ? fulfilledHandler.apply(this, value) : fulfilledHandler.call(this, value); | |
}, failedHandler); | |
}; | |
Promise.join = function (promises) { | |
promises = Array.isArray(promises) ? promises : slice.call(arguments); | |
var promise = new Promise(); | |
var length = promises.length; | |
var result = []; | |
var aggregator = function (index, obj) { | |
result[index] = obj; | |
length--; | |
if (!length) | |
promise.fulfill(result); | |
}; | |
for (var i = 0, l = length; i < l; i++) { | |
promises[i].then(aggregator.bind(null, i)); | |
} | |
return promise; | |
}; | |
Promise.group = function (promises) { | |
promises = Array.isArray(promises) ? promises : slice.call(arguments); | |
var promise = new Promise(); | |
for (var i = 0, l = promises.length; i < l; i++) { | |
promise.pipe(promises[i]); | |
} | |
return promise; | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/index.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
exports.defer = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/defer.js'); | |
exports.helpers = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/helpers.js'); | |
exports.postrouter = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/postrouter.js'); | |
exports.promise = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/promise.js'); | |
exports.environment = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/src/environment.js'); | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/src/response.js': function (require, module, exports, global, __filename, __dirname) { | |
var helpers = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/index.js').helpers; | |
var Message = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/src/message.js').Message; | |
exports.StatusCode = { | |
OK: 200, | |
CREATED: 201, | |
ACCEPTED: 202, | |
BAD_REQUEST: 400, | |
UNAUTHORIZED: 401, | |
FORBIDDEN: 403, | |
NOT_FOUND: 404, | |
METHOD_NOT_ALLOWED: 405, | |
TIMED_OUT: 408, | |
CONFLICT: 409, | |
GONE: 410, | |
INTERNAL_SERVER_ERROR: 500, | |
NOT_IMPLEMENTED: 501, | |
BAD_GATEWAY: 502, | |
SERVICE_UNAVAILABLE: 503 | |
}; | |
exports.SerializedResponse; | |
function Response(uri, status, opt_headers, opt_body) { | |
if (!(this instanceof Response)) | |
return new Response(uri, status, opt_headers, opt_body, opt_requestURI); | |
if (typeof status == 'undefined' || status == null) | |
throw new TypeError('Invalid `status` argument for Response.'); | |
Message.call(this, uri, opt_headers, opt_body); | |
this._status = status; | |
} | |
helpers.inherit(Response, Message); | |
exports.Response = Response; | |
Response.fromObject = function (object) { | |
return object && object.uri && object.status ? new Response(object.uri, object.status, object.headers, object.body) : null; | |
}; | |
Response.prototype.getMimeType = function () { | |
return this._headers['content-type']; | |
}; | |
Response.prototype.getStatusCode = function () { | |
return this._status; | |
}; | |
Response.prototype.copy = function (opt_headers, opt_body) { | |
return new Response(this._uri, this._status, this._copyHeaders(opt_headers), typeof opt_body != 'undefined' ? opt_body : this._body); | |
}; | |
Response.prototype.toJSON = function () { | |
return { | |
uri: this._uri, | |
status: this._status, | |
headers: this._headers, | |
body: this._body | |
}; | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/src/player_state.js': function (require, module, exports, global, __filename, __dirname) { | |
var helpers = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/index.js').helpers; | |
function PlayerState(stateData) { | |
Serializable.call(this, [ | |
'action', | |
'context', | |
'tracks', | |
'index', | |
'playing', | |
'loading', | |
'track', | |
'position', | |
'duration', | |
'volume', | |
'options', | |
'play_origin', | |
'next_page_url', | |
'prev_page_url' | |
]); | |
stateData = stateData || {}; | |
this.action = stateData.action; | |
this.context = stateData.context; | |
this.tracks = stateData.tracks; | |
this.index = stateData.index; | |
this.playing = stateData.playing; | |
this.loading = stateData.loading; | |
this.track = stateData.track; | |
this.position = stateData.position; | |
this.volume = stateData.volume; | |
this.duration = stateData.duration; | |
this.options = new PlayOptions(stateData.options); | |
this.play_origin = new PlayOrigin(stateData.play_origin); | |
this.next_page_url = stateData.next_page_url; | |
this.prev_page_url = stateData.prev_page_url; | |
} | |
helpers.inherit(PlayerState, Serializable); | |
PlayerState.prototype.serialize = function () { | |
if (this.options && !(this.options instanceof PlayOptions)) { | |
this.options = new PlayOptions(this.options); | |
} | |
if (this.play_origin && !(this.play_origin instanceof PlayOrigin)) { | |
this.play_origin = new PlayOrigin(this.play_origin); | |
} | |
return this._super('serialize', []); | |
}; | |
PlayerState.ACTIONS = { | |
UNKNOWN: 'unknown', | |
PLAY: 'play', | |
UPDATE: 'update', | |
STOP: 'stop', | |
RESUME: 'resume', | |
PAUSE: 'pause', | |
SKIP_PREV: 'skip_prev', | |
SKIP_NEXT: 'skip_next' | |
}; | |
function PlayOrigin(data) { | |
Serializable.call(this, [ | |
'source', | |
'reason', | |
'referrer', | |
'referrer_version', | |
'referrer_vendor' | |
]); | |
data = data || {}; | |
this.source = data.source || 'unknown'; | |
this.reason = data.reason || 'unknown'; | |
this.referrer = data.referrer || 'unknown'; | |
this.referrer_version = data.referrer_version || 'unknown'; | |
this.referrer_vendor = data.referrer_vendor || 'unknown'; | |
} | |
helpers.inherit(PlayOrigin, Serializable); | |
function PlayOptions(options) { | |
Serializable.call(this, [ | |
'repeat', | |
'shuffle', | |
'can_repeat', | |
'can_shuffle', | |
'can_skip_prev', | |
'can_skip_next', | |
'can_seek', | |
'use_dmca_rules' | |
]); | |
options = options || {}; | |
this.repeat = options.repeat !== undefined ? options.repeat : false; | |
this.shuffle = options.shuffle !== undefined ? options.shuffle : false; | |
this.can_repeat = options.can_repeat !== undefined ? options.can_repeat : true; | |
this.can_shuffle = options.can_shuffle !== undefined ? options.can_shuffle : true; | |
this.can_skip_prev = options.can_skip_prev !== undefined ? options.can_skip_prev : true; | |
this.can_skip_next = options.can_skip_next !== undefined ? options.can_skip_next : true; | |
this.can_seek = options.can_seek !== undefined ? options.can_seek : true; | |
this.use_dmca_rules = options.use_dmca_rules !== undefined ? options.use_dmca_rules : false; | |
} | |
helpers.inherit(PlayOptions, Serializable); | |
function Serializable(allowedProps) { | |
this._props = allowedProps || []; | |
} | |
Serializable.prototype.serialize = function () { | |
var data = {}; | |
var prop; | |
for (var i = 0, l = this._props.length; i < l; i++) { | |
prop = this._props[i]; | |
if (this[prop] !== undefined) { | |
if (this[prop] instanceof Serializable) { | |
data[prop] = this[prop].serialize(); | |
} else { | |
data[prop] = this[prop]; | |
} | |
} | |
} | |
return data; | |
}; | |
exports.PlayerState = PlayerState; | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/src/request.js': function (require, module, exports, global, __filename, __dirname) { | |
var helpers = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/index.js').helpers; | |
var Message = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/src/message.js').Message; | |
exports.Action = { | |
DELETE: 'DELETE', | |
GET: 'GET', | |
HEAD: 'HEAD', | |
POST: 'POST', | |
PUT: 'PUT', | |
SUB: 'SUB' | |
}; | |
exports.SerializedRequest; | |
function Request(action, uri, opt_headers, opt_body) { | |
if (!(this instanceof Request)) | |
return new Request(action, uri, opt_headers, opt_body); | |
if (!action) | |
throw new TypeError('Invalid `action` argument for Request.'); | |
Message.call(this, uri, opt_headers, opt_body); | |
this._action = action; | |
} | |
helpers.inherit(Request, Message); | |
exports.Request = Request; | |
Request.fromObject = function (object) { | |
return object && object.action && object.uri ? new Request(object.action, object.uri, object.headers, object.body) : null; | |
}; | |
Request.prototype.getAction = function () { | |
return this._action; | |
}; | |
Request.prototype.copy = function (opt_headers, opt_body) { | |
return new Request(this._action, this._uri, this._copyHeaders(opt_headers), typeof opt_body != 'undefined' ? opt_body : this._body); | |
}; | |
Request.prototype.toJSON = function () { | |
return { | |
action: this._action, | |
uri: this._uri, | |
headers: this._headers, | |
body: this._body | |
}; | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/index.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
exports.helpers = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/node_modules/js-common/index.js').helpers; | |
exports.message = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/src/message.js'); | |
exports.request = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/src/request.js'); | |
exports.response = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/src/response.js'); | |
exports.playerstate = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/src/player_state.js'); | |
}, | |
'node_modules/spotify-cosmos-api/scripts/resolver.js': function (require, module, exports, global, __filename, __dirname) { | |
var common = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/index.js'); | |
var Request = common.request.Request; | |
var Action = common.request.Action; | |
var Response = common.response.Response; | |
function _isSuccessStatus(status) { | |
return status >= 200 && status <= 299; | |
} | |
; | |
function Resolver(spResolver) { | |
if (!spResolver || typeof spResolver.resolve !== 'function') { | |
throw TypeError('Incorrect resolver argument'); | |
} | |
this._resolver = spResolver; | |
} | |
Resolver.prototype.resolve = function (request, callback) { | |
return this._resolve(request, callback); | |
}; | |
Resolver.prototype.get = function (options, callback) { | |
return this._resolveFromParams(Action.GET, options, callback); | |
}; | |
Resolver.prototype.post = function (options, callback) { | |
return this._resolveFromParams(Action.POST, options, callback); | |
}; | |
Resolver.prototype.subscribe = function (options, callback) { | |
return this._resolveFromParams(Action.SUB, options, callback); | |
}; | |
Resolver.prototype._resolve = function (request, callback) { | |
if (!callback || typeof callback !== 'function') { | |
callback = function () { | |
}; | |
} | |
function onSuccess(serverResponse) { | |
var response = Response.fromObject(serverResponse); | |
if (!response) { | |
var error = new Error('Failed to parse response: ' + JSON.stringify(serverResponse)); | |
return callback(error); | |
} | |
if (_isSuccessStatus(response.getStatusCode())) { | |
return callback(null, response); | |
} else { | |
var errorMessage = response.getHeader('error') || 'Request failed with status code ' + response.getStatusCode(); | |
var error = new Error(errorMessage); | |
error.response = response; | |
return callback(error, response); | |
} | |
} | |
function onError(serverResponse) { | |
return callback(serverResponse instanceof Error ? serverResponse : new Error('Request failed: ' + JSON.stringify(serverResponse))); | |
} | |
var resolveFn = request.getAction() === Action.SUB ? this._resolver.subscribe : this._resolver.resolve; | |
var clientRequest = resolveFn.call(this._resolver, request, onSuccess, onError); | |
return new RequestHandler(clientRequest); | |
}; | |
Resolver.prototype._resolveFromParams = function (method, options, callback) { | |
options = options || {}; | |
var url = typeof options === 'string' ? options : options.url; | |
var headers = options.headers; | |
var body = options.body; | |
var request = new Request(method, url, headers, body); | |
return this._resolve(request, callback); | |
}; | |
function RequestHandler(request) { | |
if (!request || typeof request.close !== 'function') | |
throw new TypeError('Invalid `request` argument.'); | |
this._request = request; | |
} | |
RequestHandler.prototype.cancel = function () { | |
if (this._request) { | |
this._request.close(); | |
this._request = null; | |
} | |
}; | |
exports.Resolver = Resolver; | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/spotify-deferred/node_modules/spotify-postrouter/src/postrouter.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var setImmediate = setImmediate ? setImmediate : setTimeout; | |
var CURRENT_WINDOW_ORIGIN = undefined; | |
if (typeof window !== 'undefined') { | |
CURRENT_WINDOW_ORIGIN = window.location.origin || window.location.protocol + '//' + window.location.hostname; | |
} | |
var handlers = {}; | |
var isListening = false; | |
function handleImmediateMessage(data) { | |
var handler = handlers[data.type]; | |
if (!handler) | |
return; | |
handler.fn.call(this, data); | |
} | |
function handlePostMessage(event) { | |
var data = event.data; | |
if (typeof data == 'string') { | |
try { | |
data = JSON.parse(data); | |
} catch (e) { | |
return; | |
} | |
} | |
var handler = handlers[data.type]; | |
if (!handler || handler.origin != '*' && event.origin !== handler.origin) { | |
return; | |
} | |
handler.fn.call(this, data, event); | |
} | |
var startListening = function () { | |
if (window.attachEvent && !window.addEventListener) { | |
window.attachEvent('onmessage', handlePostMessage); | |
} else { | |
window.addEventListener('message', handlePostMessage, false); | |
} | |
}; | |
var addMessageHandler = function (type, fn, origin) { | |
if (typeof window !== 'undefined' && !isListening) { | |
startListening(); | |
isListening = true; | |
} | |
if (!origin) { | |
origin = CURRENT_WINDOW_ORIGIN; | |
} | |
if (handlers[type]) { | |
throw new Error('Rehandling of message "' + type + '" not allowed.'); | |
} | |
handlers[type] = { | |
fn: fn, | |
origin: origin | |
}; | |
return; | |
}; | |
var removeMessageHandler = function (type, fn) { | |
if (handlers[type] && (!fn || handlers[type].fn === fn)) { | |
handlers[type] = null; | |
return true; | |
} | |
return false; | |
}; | |
var sendMessage = function (type, data, destWindow, origin) { | |
data = data || {}; | |
data.type = type; | |
if (typeof window === 'undefined') { | |
return setImmediate(handleImmediateMessage.bind(null, data)); | |
} | |
destWindow = destWindow || window; | |
if (!origin) { | |
origin = CURRENT_WINDOW_ORIGIN; | |
} | |
destWindow.postMessage(JSON.stringify(data), origin); | |
}; | |
module.exports = { | |
addMessageHandler: addMessageHandler, | |
removeMessageHandler: removeMessageHandler, | |
sendMessage: sendMessage, | |
WINDOW_ORIGIN: CURRENT_WINDOW_ORIGIN | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/node_modules/spotify-deferred/src/deferred.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var PostRouter = require('node_modules/spotify-cosmos-api/node_modules/spotify-deferred/node_modules/spotify-postrouter/src/postrouter.js'); | |
var deferred = []; | |
var send = function () { | |
PostRouter.sendMessage('execute_deferreds'); | |
}; | |
function executeDeferreds() { | |
var fns = deferred.splice(0); | |
if (!fns.length) | |
return; | |
for (var i = 0, l = fns.length; i < l; i++) { | |
try { | |
fns[i](); | |
} finally { | |
null; | |
} | |
} | |
} | |
PostRouter.addMessageHandler('execute_deferreds', executeDeferreds); | |
var defer = function (fn) { | |
var trigger = !deferred.length; | |
deferred.push(fn); | |
if (trigger) | |
send(); | |
}; | |
module.exports = defer; | |
}, | |
'node_modules/spotify-cosmos-api/env/request.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var defer = require('node_modules/spotify-cosmos-api/node_modules/spotify-deferred/src/deferred.js'); | |
function ClientRequest(resolver, requestId, data, onsuccess, onerror) { | |
this._requestId = requestId; | |
this._resolver = resolver; | |
this._requestData = data; | |
this._successCallback = onsuccess; | |
this._errorCallback = onerror; | |
this._status = ClientRequest.status.INITIALIZED; | |
} | |
exports.ClientRequest = ClientRequest; | |
ClientRequest.status = { | |
INITIALIZED: 'INITIALIZED', | |
CLOSED: 'CLOSED', | |
OPEN: 'OPEN' | |
}; | |
ClientRequest.messages = { | |
OPEN: 'cosmos_request_create', | |
PULL: 'cosmos_request_pull', | |
CLOSE: 'cosmos_request_cancel' | |
}; | |
ClientRequest.prototype.open = function () { | |
if (this._status === ClientRequest.status.INITIALIZED) { | |
this._status = ClientRequest.status.OPEN; | |
this._sendRequest(ClientRequest.messages.OPEN, this._requestData); | |
} | |
}; | |
ClientRequest.prototype.pull = function () { | |
if (this._status === ClientRequest.status.OPEN) { | |
this._sendRequest(ClientRequest.messages.PULL, this._requestData); | |
} | |
return this._status; | |
}; | |
ClientRequest.prototype.close = function () { | |
if (this._status === ClientRequest.status.OPEN) { | |
this._status = ClientRequest.status.CLOSE; | |
this._sendRequest(ClientRequest.messages.CLOSE); | |
} | |
}; | |
ClientRequest.prototype.onClose = function () { | |
}; | |
ClientRequest.prototype._sendRequest = function (requestName, data) { | |
this._resolver._sendRequest(requestName, this._requestId, data || {}); | |
}; | |
ClientRequest.prototype._handleResponse = function (requestName, data) { | |
var self = this; | |
var status = data && data.status; | |
var callback; | |
if (requestName === ClientRequest.messages.CLOSE) { | |
this._successCallback = null; | |
this._errorCallback = null; | |
this._requestData = null; | |
this.onClose(this._requestId); | |
return; | |
} | |
callback = this._successCallback; | |
callback = typeof callback === 'function' ? callback : function () { | |
}; | |
defer(callback.bind(this, data)); | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/env/bootstrap.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var ClientRequest = require('node_modules/spotify-cosmos-api/env/request.js').ClientRequest; | |
var resolverUID = 0; | |
function Resolver() { | |
if (!(this instanceof Resolver)) | |
return new Resolver(); | |
this._id = resolverUID++; | |
this._requestID = 0; | |
this._handlers = {}; | |
} | |
exports.Resolver = Resolver; | |
Resolver.prototype._addHandler = function (requestID, handler) { | |
this._handlers[requestID] = handler; | |
return this; | |
}; | |
Resolver.prototype._removeHandler = function (requestID) { | |
this._handlers[requestID] = null; | |
return this; | |
}; | |
Resolver.prototype._sendRequest = function (requestID, data) { | |
throw new Error('Resolver _sendRequest not implemented.'); | |
}; | |
Resolver.prototype._handleResponse = function (response) { | |
throw new Error('Resolver _handleResponse not implemented.'); | |
}; | |
Resolver.prototype._dispatchResponse = function (requestID, requestType, data) { | |
var handler = this._handlers[requestID]; | |
if (!handler) | |
return; | |
handler._handleResponse(requestType, data); | |
}; | |
Resolver.prototype._resolve = function (data, onsuccess, onerror) { | |
if (!data || !onsuccess || !onerror || typeof onsuccess != 'function' || typeof onerror != 'function') | |
throw new TypeError('Invalid argument length for `resolve`.'); | |
var requestID = ++this._requestID; | |
var request = new ClientRequest(this, requestID, data, onsuccess, onerror); | |
this._addHandler(requestID, request); | |
request.onClose = this._removeHandler.bind(this); | |
request.open(); | |
return request; | |
}; | |
Resolver.prototype.resolve = function (data, onsuccess, onerror) { | |
throw new Error('Resolver resolve not implemented.'); | |
}; | |
Resolver.prototype.subscribe = function (data, onsuccess, onerror) { | |
throw new Error('Resolver subscribe not implemented.'); | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/env/bootstrap.native.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var SpotifyApi = global.SpotifyApi; | |
var useApiRequest = !!(SpotifyApi && SpotifyApi.api && typeof SpotifyApi.api.request === 'function'); | |
var Resolver = require('node_modules/spotify-cosmos-api/env/bootstrap.js').Resolver; | |
var defer = require('node_modules/spotify-cosmos-api/node_modules/spotify-deferred/src/deferred.js'); | |
function NativeResolver(spBridge) { | |
if (!(this instanceof NativeResolver)) | |
return new NativeResolver(); | |
if (!spBridge) { | |
throw new TypeError('Missing `spBridge` parameter'); | |
} | |
Resolver.call(this); | |
this._bridge = spBridge; | |
this._deferredFlush = false; | |
} | |
NativeResolver.prototype = new Resolver(); | |
NativeResolver.prototype.constructor = NativeResolver; | |
exports.NativeResolver = NativeResolver; | |
NativeResolver.prototype._prepareCoreFlush = function () { | |
if (!this._deferredFlush) { | |
this._deferredFlush = true; | |
this._defer(this, this._flushRequests); | |
} | |
}; | |
NativeResolver.prototype._flushRequests = function () { | |
this._deferredFlush = false; | |
var flushMsg = JSON.stringify({ | |
name: 'core_flush', | |
args: [] | |
}); | |
this._sendBridgeRequest(flushMsg, { | |
onSuccess: function () { | |
}, | |
onFailure: function () { | |
} | |
}); | |
}; | |
NativeResolver.prototype._defer = function (context, callback) { | |
defer(callback.bind(context)); | |
}; | |
NativeResolver.prototype._sendRequest = function (requestName, requestId, data) { | |
var self = this; | |
data = data.serialize ? data.serialize() : data; | |
var args = [ | |
requestId, | |
data | |
]; | |
var caller = { | |
self: this, | |
id: requestId, | |
type: requestName | |
}; | |
if (useApiRequest) { | |
this._sendApiRequest(requestName, args, caller, this._handleResponse, this._handleError); | |
} else { | |
this._sendCosmosRequest(requestName, args, caller, this._handleResponse, this._handleError); | |
} | |
}; | |
NativeResolver.prototype._sendCosmosRequest = function (requestName, args, caller, onSuccess, onError) { | |
var message = JSON.stringify({ | |
name: requestName, | |
args: args | |
}); | |
this._sendBridgeRequest(message, { | |
onSuccess: function (data) { | |
onSuccess.call(caller, JSON.parse(data)); | |
}, | |
onFailure: function (data) { | |
data = JSON.parse(data); | |
onError.call(caller, data); | |
} | |
}); | |
this._prepareCoreFlush(); | |
}; | |
NativeResolver.prototype._sendBridgeRequest = function (message, callbackMap) { | |
this._bridge.executeRequest(message, callbackMap || {}); | |
}; | |
NativeResolver.prototype._sendApiRequest = function (requestName, args, caller, onSuccess, onError) { | |
SpotifyApi.api.request(requestName, args, caller, onSuccess, onError); | |
}; | |
NativeResolver.prototype._handleResponse = function (data) { | |
this.self._dispatchResponse(this.id, this.type, data.responses && data.responses[0] || data); | |
}; | |
NativeResolver.prototype._handleError = function (error) { | |
this.self._dispatchResponse(this.id, this.type, error); | |
}; | |
NativeResolver.prototype.resolve = function (data, onsuccess, onerror) { | |
function onResult(callback, response) { | |
this._defer(this, callback.bind(this, response)); | |
request.close(); | |
} | |
var request = this._resolve(data, onResult.bind(this, onsuccess), onResult.bind(this, onerror)); | |
return request; | |
}; | |
NativeResolver.prototype.subscribe = function (data, onsuccess, onerror) { | |
function onResult(callback, response) { | |
callback.call(this, response); | |
request.pull(); | |
} | |
var request = this._resolve(data, onResult.bind(this, onsuccess), onResult.bind(this, onerror)); | |
return request; | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/env/bootstrap.web.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var defer = require('node_modules/spotify-cosmos-api/node_modules/spotify-deferred/src/deferred.js'); | |
var Resolver = require('node_modules/spotify-cosmos-api/env/bootstrap.js').Resolver; | |
function WebResolver(opt_target) { | |
if (!(this instanceof WebResolver)) | |
return new WebResolver(opt_target); | |
Resolver.call(this); | |
this._target = opt_target || '*'; | |
this._handleResponse = this._handleResponse.bind(this); | |
this._requestMessageType = 'cosmos-request'; | |
this._responseMessageType = 'cosmos-response'; | |
this._requestIdPrefix = 'cosmos_'; | |
this.attach(); | |
} | |
WebResolver.prototype = new Resolver(); | |
WebResolver.prototype.constructor = WebResolver; | |
exports.WebResolver = WebResolver; | |
WebResolver.prototype._sendRequest = function (requestName, requestID, data) { | |
var top = global.window.top; | |
var message = { | |
type: this._requestMessageType, | |
resolver: this._id, | |
id: this._requestIdPrefix + requestID, | |
name: requestName, | |
payload: data.serialize ? data.serialize() : data | |
}; | |
top.postMessage(JSON.stringify(message), this._target); | |
}; | |
WebResolver.prototype._handleResponse = function (response) { | |
var data = response.data; | |
if (typeof data == 'string') { | |
try { | |
data = JSON.parse(response.data); | |
} catch (e) { | |
return; | |
} | |
} | |
if (data.type != this._responseMessageType || data.resolver != this._id || !data.payload) | |
return; | |
var id = data.id || ''; | |
var requestID = parseInt(id.replace(this._requestIdPrefix, ''), 10); | |
var requestName = data.name || ''; | |
if (!requestID || !requestName) | |
return; | |
this._dispatchResponse(requestID, requestName, data.payload); | |
}; | |
WebResolver.prototype.attach = function () { | |
var win = global.window; | |
if (win.addEvent && !win.addEventListener) { | |
win.addEvent('onmessage', this._handleResponse); | |
} else { | |
win.addEventListener('message', this._handleResponse, false); | |
} | |
}; | |
WebResolver.prototype.detach = function () { | |
var win = global.window; | |
if (win.removeEvent && !win.removeEventListener) { | |
win.removeEvent('onmessage', this._handleResponse); | |
} else { | |
win.removeEventListener('message', this._handleResponse, false); | |
} | |
}; | |
WebResolver.prototype.resolve = function (data, onsuccess, onerror) { | |
function onResult(callback, response) { | |
defer(callback.bind(this, response)); | |
request.close(); | |
} | |
var request = this._resolve(data, onResult.bind(this, onsuccess), onResult.bind(this, onerror)); | |
return request; | |
}; | |
WebResolver.prototype.subscribe = function (data, onsuccess, onerror) { | |
return this._resolve(data, onsuccess, onerror); | |
}; | |
}, | |
'node_modules/spotify-cosmos-api/index.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var window = global.window || {}; | |
var process = global.process; | |
var common = require('node_modules/spotify-cosmos-api/node_modules/cosmos-common-js/index.js'); | |
var Resolver = require('node_modules/spotify-cosmos-api/scripts/resolver.js').Resolver; | |
var SPResolver = null; | |
var spResolver = null; | |
var hasNativeBridge = window._getSpotifyModule && typeof window._getSpotifyModule === 'function' && window._getSpotifyModule('bridge'); | |
var isNodeJs = process && process.title && process.title.match(/node$/); | |
if (!isNodeJs) { | |
if (hasNativeBridge) { | |
SPResolver = require('node_modules/spotify-cosmos-api/env/bootstrap.native.js').NativeResolver; | |
spResolver = new SPResolver(hasNativeBridge); | |
} else { | |
SPResolver = require('node_modules/spotify-cosmos-api/env/bootstrap.web.js').WebResolver; | |
spResolver = new SPResolver(); | |
} | |
} | |
exports.Resolver = Resolver; | |
exports.Action = common.request.Action; | |
exports.Request = common.request.Request; | |
exports.Response = common.response.Response; | |
exports.resolver = spResolver ? new Resolver(spResolver) : null; | |
}, | |
'node_modules/spotify-live-models/util/cosmos.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var Emitter = require('node_modules/prime/emitter.js'); | |
var live = require('node_modules/spotify-live/index.js'); | |
var isNodeJs = process && process.title && process.title.match(/node$/); | |
var cosmos = {}; | |
if (!isNodeJs) { | |
cosmos = require('node_modules/spotify-cosmos-api/index.js'); | |
} | |
var ASAP = live.ASAP; | |
function DELETE(options, callback) { | |
options.method = exports.cosmos.Action.DELETE; | |
return request(options, callback); | |
} | |
function GET(options, callback) { | |
options.method = exports.cosmos.Action.GET; | |
return request(options, callback); | |
} | |
function SUB(options, callback) { | |
options.method = exports.cosmos.Action.SUB; | |
return request(options, callback); | |
} | |
function POST(options, callback) { | |
options.method = exports.cosmos.Action.POST; | |
return request(options, callback); | |
} | |
function PUT(options, callback) { | |
options.method = exports.cosmos.Action.PUT; | |
return request(options, callback); | |
} | |
function HEAD(options, callback) { | |
options.method = exports.cosmos.Action.HEAD; | |
return request(options, callback); | |
} | |
function request(options, callback) { | |
var method = options.method; | |
delete options.method; | |
var subscription, canceled; | |
sanitizeURL(options.url, function (error, url) { | |
if (error) | |
return callback && callback(error); | |
if (canceled) | |
return; | |
var request = new exports.cosmos.Request(method || exports.cosmos.Action.GET, url, options.headers, options.body); | |
subscription = exports.cosmos.resolver.resolve(request, function (error, response) { | |
if (!callback) | |
return; | |
if (error) | |
return callback(error); | |
try { | |
callback(null, { | |
body: response.getJSONBody(), | |
headers: response.getHeaders(), | |
status: response.getStatusCode() | |
}); | |
} catch (parseError) { | |
callback(parseError, response); | |
} | |
}); | |
}); | |
return { | |
cancel: function () { | |
if (subscription && subscription.cancel) { | |
subscription.cancel(); | |
subscription = null; | |
} else if (!canceled) { | |
canceled = true; | |
} | |
return null; | |
} | |
}; | |
} | |
function sanitizeURL(url, callback) { | |
if (url.indexOf('@') > -1) { | |
live('spotify:client').query('currentUser(username)', function (error, data) { | |
if (error) | |
return callback(error); | |
callback(null, url.replace('@', encodeURIComponent(data.currentUser.username))); | |
}, ASAP); | |
} else { | |
callback(null, url); | |
} | |
} | |
exports.request = request; | |
exports.get = GET; | |
exports.post = POST; | |
exports.subscribe = SUB; | |
exports.delete = DELETE; | |
exports.put = PUT; | |
exports.head = HEAD; | |
exports.cosmos = cosmos; | |
exports.sanitizeURL = sanitizeURL; | |
}, | |
'node_modules/spotify-live-models/add/collection.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var prime = require('node_modules/prime/index.js'); | |
var defer = require('node_modules/prime/defer.js'); | |
var live = require('node_modules/spotify-live/index.js'); | |
var contains = require('node_modules/mout/array/contains.js'); | |
var forEach = require('node_modules/mout/array/forEach.js'); | |
var map = require('node_modules/mout/array/map.js'); | |
var bind = require('node_modules/mout/function/bind.js'); | |
var cosmos = require('node_modules/spotify-live-models/util/cosmos.js'); | |
var Collection = prime({ | |
constructor: function (endpoints) { | |
this.endpoints = endpoints; | |
this.onPublish = bind(this.onPublish, this); | |
this.waitQueue = []; | |
this.onWait = bind(this.onWait, this); | |
this.fetch = bind(this.fetch, this); | |
}, | |
onPublish: function (model, data) { | |
if ('isInCollection' in data) { | |
var endpoints = this.endpoints; | |
var uri = model.uri; | |
var state = !!data.isInCollection; | |
var method = state ? 'post' : 'delete'; | |
cosmos[method]({ | |
url: endpoints.updateIsInCollection, | |
body: [uri.substr(-22)] | |
}, function (error, data) { | |
if (error) { | |
model.update({ isInCollection: !state }); | |
} else { | |
cosmos.post({ | |
url: endpoints.broadcast, | |
body: { | |
uri: uri, | |
isInCollection: state | |
} | |
}); | |
if (data && data.body.items) { | |
forEach(data.body.items, function (item) { | |
cosmos.post({ | |
url: endpoints.broadcast, | |
body: item | |
}); | |
}); | |
} | |
} | |
}); | |
} | |
}, | |
onWait: function (model, properties) { | |
if (contains(properties, 'isInCollection')) { | |
this.fetch(model); | |
} | |
}, | |
fetch: function (model) { | |
var endpoints = this.endpoints; | |
if (!model.id) | |
model.id = model.uri.substr(-22); | |
var queue = this.waitQueue; | |
if (queue.push(model) === 1) | |
defer(function () { | |
this.waitQueue = []; | |
cosmos.post({ | |
url: endpoints.isInCollection, | |
body: map(queue, 'id') | |
}, function (error, result) { | |
if (error) | |
throw error; | |
forEach(queue, function (model, i) { | |
model.update({ isInCollection: result.body[i] }); | |
}); | |
}); | |
}, this); | |
} | |
}); | |
Collection.onBroadcast = function (error, response) { | |
if (error) | |
throw error; | |
live(response.body.uri).update({ isInCollection: response.body.isInCollection }); | |
}; | |
module.exports = Collection; | |
}, | |
'node_modules/spotify-live-models/add/track.js': function (require, module, exports, global, __filename, __dirname) { | |
'use strict'; | |
var live = require('node_modules/spotify-live/index.js'); | |
var forEach = require('node_modules/mout/array/forEach.js'); | |
var cosmos = require('node_modules/spotify-live-models/util/cosmos.js'); | |
var Collection = require('node_modules/spotify-live-models/add/collection.js'); | |
var endpoints = { | |
isInCollection: 'hm://collection-web/v1/@/contains/tracks', | |
updateIsInCollection: 'hm://collection-web/v1/@/tracks', | |
broadcast: 'sp://messages/v1/collectionstate', | |
pubsub: 'hm://collection/collection/@/json' | |
}; | |
var trackCollection = new Collection(endpoints); | |
var isRegistered = false; | |
var broadcastSubscription; | |
var collectionSubscription; | |
var onCollectionPublish = function (error, response) { | |
if (error) | |
throw error; | |
var items = response.body.items; | |
forEach(items, function (item) { | |
if (item.type.toUpperCase() === 'TRACK') { | |
var uri = 'spotify:track:' + item.identifier; | |
live(uri).update({ isInCollection: !item.removed }); | |
} | |
}); | |
}; | |
var regExp = exports.matches = /^spotify:track:[^:]+$/; | |
exports.register = function () { | |
if (isRegistered) | |
return; | |
live.subscribe(regExp, 'publish', trackCollection.onPublish); | |
live.subscribe(regExp, 'wait', trackCollection.onWait); | |
broadcastSubscription = cosmos.subscribe({ url: endpoints.broadcast }, Collection.onBroadcast); | |
collectionSubscription = cosmos.subscribe({ url: endpoints.pubsub }, onCollectionPublish); | |
isRegistered = true; | |
}; | |
exports.unregister = function () { | |
if (!isRegistered) | |
return; | |
live.unsubscribe(regExp, 'publish', trackCollection.onPublish); | |
live.unsubscribe(regExp, 'wait', trackCollection.onWait); | |
if (broadcastSubscription) { | |
broadcastSubscription.cancel(); | |
broadcastSubscription = null; | |
} | |
if (collectionSubscription) { | |
collectionSubscription.cancel(); | |
collectionSubscription = null; | |
} | |
isRegistered = false; | |
}; | |
exports._endpoints = endpoints; | |
}, | |
'scripts/player.js': function (require, module, exports, global, __filename, __dirname) { | |
var AudioAd = require('node_modules/revgen-shared/scripts/audioad.js').AudioAd; | |
var AdBreak = require('node_modules/revgen-shared/scripts/ad_break.js'); | |
var addEvents = require('node_modules/spotify-events/add.js'); | |
var live = require('node_modules/spotify-live/index.js'); | |
live.register(require('node_modules/spotify-live-models/add/track.js')); | |
live.register(require('node_modules/spotify-live-models/client.js')); | |
(function (models, events, suggest, Image, pu, widgets, ShuffleButton, RepeatButton, PlayPauseButton, nextBackButtons, VolumeControl, ProgressBar, tracking, previewPlayer, ContextApp, Logger, localeStrings) { | |
var playerUtils = pu.playerUtils; | |
var trackHistory = pu.trackHistory; | |
var konco = pu.kc; | |
var _ = localeStrings.get.bind(localeStrings); | |
var logger = Logger.forTag('PlayerApp'); | |
var adBreak = new AdBreak(); | |
var currentBodyWidth = document.body.offsetWidth; | |
var player = models.player; | |
var track = models.Track; | |
var id = playerUtils.getId; | |
var playerUID = -1; | |
var trackName = id('track-name'); | |
var addCollectionButton = document.getElementById('track-add'); | |
var queueButton = document.getElementById('queue'); | |
var widgetMore = document.getElementById('widget-more'); | |
var trackNameMarquee = new pu.Marquee(trackName, currentBodyWidth - 40); | |
var eventManager = new events.EventManager(); | |
var artistWidget = new widgets.ArtistWidget(id('track-artist'), currentBodyWidth - 40, eventManager, logger); | |
var playBtn = new PlayPauseButton('play-pause', logger, adBreak); | |
var nextBtn = new nextBackButtons.NextButton('next', eventManager, logger, adBreak); | |
var backBtn = new nextBackButtons.BackButton('previous', eventManager, logger, adBreak); | |
var shuffleBtn = new ShuffleButton('shuffle', logger, adBreak); | |
var repeatBtn = new RepeatButton('repeat', logger, adBreak); | |
var progressBar = new ProgressBar('progress', eventManager, logger, adBreak); | |
var imageWrapper = id('cover-art'); | |
var coverImage = null; | |
var volumeControl = new VolumeControl('volume', logger); | |
var currentTrack = null; | |
var history = new trackHistory(3); | |
var suggestionArea = id('suggestions'); | |
var suggestList = suggestionArea.querySelector('ul'); | |
var tracker = new tracking.Tracker(); | |
var volumeBarWrapper = document.getElementById('volume'); | |
var volumeShowTrigger = document.getElementById('volume-show'); | |
var volumeCloseTimer = null; | |
var miniPlayer = null; | |
var appLinkBlacklist = [ | |
'now-playing-recs', | |
'discover', | |
'spotify-web-player', | |
'context-actions', | |
'suggest' | |
]; | |
models.Loadable.define(models.Player, [ | |
'__length', | |
'__rules', | |
'__index', | |
'__owner', | |
'__uid' | |
], '_playapp'); | |
var getPlayerLink = function () { | |
var ownerUri = player.__owner && player.__owner.toSpotifyURI() || ''; | |
var ownerApp = playerUtils.appNameFromUri(ownerUri); | |
if (appLinkBlacklist.indexOf(ownerApp) > -1) { | |
ownerUri = player.track.album && player.track.album.uri || ''; | |
} | |
if (player.track && player.track.advertisement) { | |
ownerUri = player.track.artists[0].uri || ownerUri; | |
} | |
if (adBreak.inProgress() && adBreak.getDetails() && adBreak.getDetails().clickUrl) { | |
ownerUri = adBreak.getDetails().clickUrl; | |
} | |
return ownerUri; | |
}; | |
var updateArtwork = function () { | |
var adBreakImage = adBreak.inProgress() && adBreak.getDetails() ? adBreak.getDetails().imageUrl : null; | |
if (coverImage) { | |
coverImage.setImage(adBreakImage || player.track); | |
coverImage.setLink(getPlayerLink()); | |
} else { | |
var createImage = adBreakImage ? Image.fromSource : player.track ? Image.forTrack : Image.forAlbum; | |
coverImage = createImage(adBreakImage || player.track || currentTrack.album, { | |
animate: true, | |
height: 210, | |
width: 210, | |
style: 'plain', | |
quickActionMenu: false, | |
link: getPlayerLink() | |
}); | |
imageWrapper.appendChild(coverImage.node); | |
} | |
}; | |
var handleAdClick = function () { | |
if (adBreak.inProgress()) { | |
var adURI = currentTrack.advertisement ? currentTrack.artists[0].uri : adBreak.getDetails() ? adBreak.getDetails().clickUrl : null; | |
if (currentTrack.advertisement && currentTrack.ad_metadata) { | |
AudioAd.handleAdClick(currentTrack.ad_metadata, currentTrack.advertisement); | |
} else { | |
AudioAd.handleAdClick(adURI, currentTrack.advertisement); | |
} | |
return false; | |
} | |
return true; | |
}; | |
var contextEndSuggestions = function (suggestion) { | |
suggestList.innerHTML = ''; | |
var newSuggestions = document.createDocumentFragment(); | |
if (history.size() === 0) { | |
return false; | |
} | |
suggest.getTrackSuggestions(history.show('uri'), 3, function (result) { | |
var trackIDs = result[0].gids; | |
for (var i = 0; i < 3; i++) { | |
track.fromURI('spotify:track:' + trackIDs[i]).load('name', 'image').done(function (track) { | |
buildSuggestion(track); | |
}); | |
} | |
}, function () { | |
if (history.size() < 3) { | |
history.get(0).album.load([ | |
'name', | |
'uri', | |
'image' | |
]).done(function (album) { | |
buildSuggestion(album); | |
}); | |
} | |
history.forEach(buildSuggestion); | |
}); | |
var buildSuggestion = function (suggestion) { | |
var item = document.createElement('li'); | |
var isTrack = suggestion instanceof models.Track; | |
var isAlbum = suggestion instanceof models.Album; | |
var createImage = isTrack ? Image.forTrack : isAlbum ? Image.forAlbum : false; | |
if (!createImage) | |
return; | |
var image = createImage(suggestion, { | |
animate: true, | |
height: 50, | |
width: 50, | |
quickActionMenu: false, | |
style: 'plain' | |
}); | |
item.appendChild(image.node); | |
var itemText = document.createElement('p'); | |
var artistText = document.createElement('span'); | |
itemText.innerHTML = suggestion.name; | |
suggestion.artists[0].load('name').done(function (artist) { | |
artistText.innerHTML += artist.name.decodeForHtml(); | |
item.appendChild(itemText); | |
item.appendChild(artistText); | |
item.setAttribute('data-uri', suggestion.uri); | |
playerUtils.addEventSimple(item, 'click', function (e) { | |
e.preventDefault(); | |
models.application.openURI(this.getAttribute('data-uri').replace('spotify:', 'spotify:radio:')); | |
}); | |
suggestList.appendChild(item); | |
}); | |
}; | |
suggestionArea.appendChild(suggestList); | |
logger.userImpression('context_end_suggestions'); | |
playerUtils.addClass(suggestionArea, 'active'); | |
window.setTimeout(function () { | |
playerUtils.addClass(suggestList, 'active'); | |
}, 300); | |
}; | |
var resetPlayer = function () { | |
if (currentTrack === null) { | |
return; | |
} | |
contextEndSuggestions(); | |
imageWrapper.innerHTML = ''; | |
coverImage = null; | |
artistWidget.clear(); | |
trackName.innerHTML = ' '; | |
currentTrack = null; | |
progressBar.setPageTitle(); | |
playerUtils.addClass(document.body, 'noplayback'); | |
nextBtn.disableButton(); | |
backBtn.disableButton(); | |
playBtn.disableButton(); | |
}; | |
var handleAdBreakStartEnd = function () { | |
nextBtn.update(); | |
backBtn.update(); | |
playBtn.updateStatus(); | |
if (adBreak.inProgress()) { | |
progressBar.disable(); | |
shuffleBtn.disableButton(); | |
repeatBtn.disableButton(); | |
playerUtils.addClass(widgetMore, 'disabled'); | |
playerUtils.addClass(document.body, 'isannons'); | |
} else { | |
handleTrackChange(); | |
updateArtwork(); | |
} | |
}; | |
var handleAdBreakDetails = function () { | |
updateArtwork(); | |
var d = adBreak.getDetails() || {}; | |
if (d.title) { | |
id('track-name').innerHTML = '<a href="' + d.clickUrl + '" target="_blank">' + d.title + '</a>'; | |
trackNameMarquee.refresh(); | |
} | |
if (d.description) { | |
id('track-artist').innerHTML = '<a href="' + d.clickUrl + '" target="_blank">' + d.description + '</a>'; | |
artistWidget.marquee.refresh(); | |
} | |
}; | |
var handleTrackChange = function (e) { | |
if (!player.track || e && !e.target.track) { | |
return false; | |
} | |
playerUtils.removeClass(document.body, 'noplayback'); | |
playerUtils.removeClass(suggestionArea, 'active'); | |
playerUtils.removeClass(suggestList, 'active'); | |
track.fromURI(player.track.uri).load([ | |
'name', | |
'uri', | |
'image', | |
'duration', | |
'artists', | |
'starred', | |
'album' | |
]).done(function (track) { | |
if (!(adBreak.inProgress() && adBreak.getDetails())) { | |
var trackNameLink = document.createElement('a'); | |
trackName.innerHTML = ''; | |
trackNameLink.href = track.uri.toSpotifyURL(); | |
trackNameLink.innerHTML = track.name.decodeForHtml(); | |
trackName.appendChild(trackNameLink); | |
trackNameMarquee.refresh(); | |
} | |
addCollectionButton.setAttribute('data-uri', track.uri); | |
addEvents.update(); | |
if (!adBreak.inProgress()) { | |
history.add(track); | |
progressBar.enable(); | |
playerUtils.removeClass(document.body, 'isannons'); | |
playerUtils.removeClass(widgetMore, 'disabled'); | |
} else { | |
progressBar.disable(); | |
playerUtils.addClass(widgetMore, 'disabled'); | |
playerUtils.addClass(document.body, 'isannons'); | |
} | |
if (!currentTrack || currentTrack.image !== track.image) { | |
updateArtwork(); | |
} | |
currentTrack = track; | |
if (adBreak.inProgress()) { | |
updateArtwork(); | |
} | |
if (coverImage) { | |
coverImage.node.setAttribute('data-itemuri', currentTrack.uri); | |
} | |
eventManager.trigger(eventManager.Events.TRACK_CHANGED, track); | |
}); | |
}; | |
var updatePlayer = function (e) { | |
var nowPlayingTrack = player.track; | |
if (playerUID !== player.__uid) { | |
playerUID = player.__uid; | |
handleTrackChange(); | |
} | |
volumeCheck(); | |
if (nowPlayingTrack === null) { | |
resetPlayer(); | |
return false; | |
} | |
if (miniPlayer) { | |
postMiniPlayerState(); | |
} | |
}; | |
var volumeCheck = function () { | |
var nowPlayingTrack = player.track; | |
if (nowPlayingTrack && (nowPlayingTrack.advertisement || nowPlayingTrack.uri.indexOf('spotify:ad') !== -1)) { | |
if (player.volume == 0) { | |
player.pause(); | |
} else { | |
if (player.paused) { | |
player.play(); | |
} | |
} | |
} | |
}; | |
var starUnstar = function () { | |
if (!currentTrack.starred) { | |
currentTrack.star().done(function (track) { | |
postMiniPlayerState(); | |
}).fail(function () { | |
}); | |
} else { | |
currentTrack.unstar().done(function () { | |
postMiniPlayerState(); | |
}); | |
} | |
}; | |
var handleWindowResize = function () { | |
volumeControl.reload(); | |
progressBar.reload(); | |
trackNameMarquee.widthAdjust(currentBodyWidth - 40); | |
artistWidget.resize(currentBodyWidth - 40); | |
}; | |
var postMiniPlayerState = function () { | |
messageMiniPlayer(player); | |
}; | |
var messageMiniPlayer = function (obj) { | |
if (miniPlayer) { | |
miniPlayer.postMessage(JSON.stringify(obj), '*'); | |
} | |
}; | |
var handleMiniPlayerMessage = function (msg) { | |
switch (msg.miniplayer) { | |
case 'staterequest': | |
postMiniPlayerState(); | |
break; | |
case 'playpause': | |
playBtn.toggle(); | |
break; | |
case 'next': | |
nextBtn.playerNext(); | |
break; | |
case 'back': | |
backBtn.playerBack(); | |
break; | |
case 'shuffle': | |
shuffleBtn.toggle(); | |
break; | |
case 'repeat': | |
repeatBtn.toggle(); | |
break; | |
case 'star': | |
starUnstar(); | |
break; | |
case 'seek': | |
player.seek(msg.target); | |
break; | |
case 'volume': | |
player.setVolume(msg.target); | |
break; | |
case 'openuri': | |
models.application.openURI(msg.target); | |
break; | |
default: | |
break; | |
} | |
}; | |
var setupVolumeShowHide = function () { | |
volumeShowTrigger.addEventListener('mouseout', function (e) { | |
e.stopPropagation(); | |
playerUtils.addClass(volumeBarWrapper, 'show'); | |
if (volumeCloseTimer !== null) { | |
clearTimeout(volumeCloseTimer); | |
} | |
volumeCloseTimer = setTimeout(function () { | |
playerUtils.removeClass(volumeBarWrapper, 'show'); | |
}, 1000); | |
}, false); | |
window.addEventListener('mouseout', function (e) { | |
if (!e.toElement) { | |
playerUtils.removeClass(volumeBarWrapper, 'show'); | |
} | |
}, false); | |
}; | |
var setupLangStrings = function () { | |
document.querySelector('#suggestions p').textContent = _('start-radio-suggestion'); | |
}; | |
var init = function () { | |
setupLangStrings(); | |
logger.userImpression('player_loaded'); | |
addEvents.attach(); | |
adBreak.addEventListener('startBreak', handleAdBreakStartEnd); | |
adBreak.addEventListener('endBreak', handleAdBreakStartEnd); | |
adBreak.addEventListener('receiveDetails', handleAdBreakDetails); | |
adBreak.init(player); | |
queueButton.addEventListener('click', function (e) { | |
e.preventDefault(); | |
models.application.openURI('spotify:app:queue'); | |
}, false); | |
player.load([ | |
'track', | |
'__uid', | |
'__owner', | |
'__rules', | |
'position', | |
'playing' | |
]).done(function () { | |
playerUtils.addEventSimple(trackName, 'click', function (e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
logger.userHit('track_link', { track_id: currentTrack.uri }); | |
if (e.target.href) { | |
if (adBreak.inProgress()) { | |
handleAdClick(); | |
return false; | |
} | |
models.application.openURI(getPlayerLink()); | |
} | |
}); | |
playerUtils.addEventSimple(imageWrapper, 'click', function (e) { | |
e.preventDefault(); | |
logger.userHit('album_link', { track_id: currentTrack.uri }); | |
handleAdClick(); | |
}); | |
playerUtils.addEventSimple(imageWrapper, 'contextmenu', function (e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
var openWith = models.fromURI(currentTrack.uri); | |
models.client.showContextUI([openWith], { | |
x: e.clientX, | |
y: e.clientY | |
}); | |
}); | |
updatePlayer(); | |
player.addEventListener('change', updatePlayer); | |
player.addEventListener('change:volume', volumeCheck); | |
artistWidget.init(); | |
trackNameMarquee.init(); | |
window.addEventListener('resize', function () { | |
currentBodyWidth = document.body.offsetWidth; | |
handleWindowResize(); | |
}, false); | |
window.addEventListener('message', function (e) { | |
if (e.source === miniPlayer) { | |
handleMiniPlayerMessage(JSON.parse(e.data)); | |
} | |
}, false); | |
eventManager.subscribe(eventManager.Events.AD_CLICKED, handleAdClick); | |
widgetMore.addEventListener('click', function (e) { | |
e.preventDefault(); | |
if (!currentTrack || playerUtils.hasClass(this, 'disabled')) { | |
return; | |
} | |
logger.userHit('context_menu_button', { track_id: currentTrack.uri }); | |
ContextApp.show('context-actions', [currentTrack.uri], e.target, 'spotify:temp-playlist:hellothere'); | |
}, false); | |
setupVolumeShowHide(); | |
models.application.hideLoadingScreen(); | |
var windowFeatures = 'menubar=no,location=no,resizable=no,scrollbars=no,status=no,width=420,height=150'; | |
var weeegg = new konco(); | |
weeegg.onSuccess = function () { | |
miniPlayer = window.open('vendor/mini/mini.html', 'Spotify', windowFeatures); | |
}; | |
weeegg.init(); | |
}); | |
previewPlayer.init(); | |
}; | |
exports.init = init; | |
}(require('node_modules/api/scripts/models.js'), require('scripts/player.events.js'), require('scripts/player.suggestions.js'), require('node_modules/views/scripts/image.js').Image, require('scripts/player-utils.js'), require('scripts/player.widgets.js'), require('scripts/player.shufflebutton.js').ShuffleButton, require('scripts/player.repeatbutton.js').RepeatButton, require('scripts/player.playpausebutton.js').PlayPauseButton, require('scripts/player.nextbackbuttons.js'), require('scripts/player.volumecontrol.js').VolumeControl, require('scripts/player.progressbar.js').ProgressBar, require('scripts/player.tracking.js'), require('scripts/preview.player.js'), require('node_modules/views/scripts/contextapp.js').ContextApp, require('node_modules/logging-utils/scripts/logger.js').Logger, require('@loc.loc/strings/main.lang'))); | |
}, | |
'scripts/main.js': function (require, module, exports, global, __filename, __dirname) { | |
(function (player) { | |
player.init(); | |
}(require('scripts/player.js'))); | |
} | |
})); // QuickStart 0.9.1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment